diff --git a/day-0/files.go b/day-0/files.go index 95dee96..5156e6c 100644 --- a/day-0/files.go +++ b/day-0/files.go @@ -6,6 +6,14 @@ import ( "os" ) +// func short_read() { +// dat, err := os.Open("./short_file.txt") +// tryMe(err) +// defer dat.Close() + +// bytes := ?? +// } + func tryMe(e error) { if e != nil { fmt.Println("OH SNAP!") diff --git a/day-0/short_file.txt b/day-0/short_file.txt new file mode 100644 index 0000000..85df507 --- /dev/null +++ b/day-0/short_file.txt @@ -0,0 +1 @@ +abcd \ No newline at end of file diff --git a/day-14/day-14 b/day-14/day-14 deleted file mode 100755 index eb3baab..0000000 Binary files a/day-14/day-14 and /dev/null differ diff --git a/day-16/main.go b/day-16/main.go new file mode 100644 index 0000000..ff0799a --- /dev/null +++ b/day-16/main.go @@ -0,0 +1,268 @@ +/* Day 16: Packet Decoder */ +package main + +import ( + "AoC2021/utils/args" + "AoC2021/utils/bits" + "AoC2021/utils/color" + "AoC2021/utils/files" + "AoC2021/utils/log" + "AoC2021/utils/timer" + "bufio" + "fmt" + "math" + "os" + "strings" +) + +func check(e error) { + if e != nil { + log.Println(log.DEBUG, "Oh snap!") + panic(e) + } +} + +func main() { + argMap := args.Parse(os.Args) + log.SetLogLevelFromArgs(argMap) + selectedPart, partSpecified := argMap["part"] + + log.Println(log.NORMAL, "--- Day 16: Packet Decoder ---") + timer.Start() + + if selectedPart == "parse" { + parseInputFile() + } + if !partSpecified || selectedPart == "1" { + part1() + timer.Tick() + log.Println(log.NORMAL) + } + + if !partSpecified || selectedPart == "2" { + part2() + timer.Tick() + log.Println(log.NORMAL) + } +} + +func part1() { + log.Println(log.NORMAL, "* Part 1 *") + log.Println(log.NORMAL, " Goal: For now, parse the hierarchy of the packets throughout the transmission. Decode the structure of your hexadecimal-encoded BITS transmission") + log.Println(log.NORMAL, " Answer: what do you get if you add up the version numbers in all packets?") + + input, err := os.Open("./input.txt") + check(err) + defer input.Close() + + bitReader := NewBitReader(input) + p, err := parsePacket(&bitReader, 0) + check(err) + + scanner := bufio.NewScanner(bitReader.file) + if scanner.Scan() { + bytesLeft := len(scanner.Bytes()) + log.Printf(log.NORMAL, color.Yellow+"Completed reading from file with %d bytes remaining!\n"+color.Reset, bytesLeft) + } + + log.Printf(log.NORMAL, "\nTotal of all versions is %d\n", p.version+p.subVersions) +} + +func part2() { + log.Println(log.NORMAL, "* Part 2 *") + log.Println(log.NORMAL, " Goal: ") + log.Println(log.NORMAL, " Answer: ") +} + +func parseInputFile() { + input, err := os.Open("./input.txt") + check(err) + defer input.Close() + + output, err := files.CreateOrReplace("./output.txt") + check(err) + defer output.Close() + + scanner := bufio.NewScanner(input) + for scanner.Scan() { + bytes := scanner.Bytes() + for _, b := range bytes { + outBits, err := bits.HexByteToBits(b) + if err != nil { + panic(fmt.Sprintf("Error parsing byte %s", string(b))) + } + for _, ob := range outBits { + if ob { + output.WriteString("1") + } else { + output.WriteString("0") + } + } + } + } +} + +type Packet struct { + id int + value int + subValues int + version int + subVersions int + pType int + subpacketCount int + bitCount int + subBitCount int +} + +var packetNum = 0 + +func parsePacket(bitReader *BitReader, depth int) (Packet, error) { + packetNum++ + p := Packet{packetNum, 0, 0, 0, 0, 0, 0, 0, 0} + versionBits, err := bitReader.ReadBits(3) + p.bitCount += 3 + if err != nil { + return p, fmt.Errorf("%s for version bits", err.Error()) + } + p.version = bits.ToInt(versionBits) + typeBits, err := bitReader.ReadBits(3) + p.bitCount += 3 + if err != nil { + return p, fmt.Errorf("%s for type bits", err.Error()) + } + p.pType = bits.ToInt(typeBits) + + log.Printf(log.DIAGNOSTIC, "\nP: %3d %s", packetNum, strings.Repeat(" ", depth*2)) + log.Printf(log.DIAGNOSTIC, "Ver %d, Type %d", p.version, p.pType) + + switch p.pType { + case 4: + log.Printf(log.DIAGNOSTIC, ": L") + var litBits []bool + var bitVals []bool + // read sets of 5 bits until the first bit of 5 is not a '1' + for bitVals, err = bitReader.ReadBits(5); bitVals[0]; bitVals, err = bitReader.ReadBits(5) { + p.bitCount += 5 + if err != nil { + return p, fmt.Errorf("%s for literal bits", err.Error()) + } + litBits = append(litBits, bitVals[1:]...) + } + // add the last set to the literal value + p.bitCount += 5 + litBits = append(litBits, bitVals[1:]...) + p.value = bits.ToInt(litBits) + log.Printf(log.DIAGNOSTIC, " (value %d)", p.value) + + return p, nil + default: + log.Printf(log.DIAGNOSTIC, ": O") + hasSubpackets, err := bitReader.ReadBit() + p.bitCount += 1 + if err != nil { + return p, fmt.Errorf("%s for subpacket bit", err.Error()) + } + if hasSubpackets { + countBits, err := bitReader.ReadBits(11) + p.bitCount += 11 + if err != nil { + return p, fmt.Errorf("%s for subpacket count", err.Error()) + } + p.subpacketCount = bits.ToInt(countBits) + log.Printf(log.DIAGNOSTIC, " (count: %d) {", p.subpacketCount) + + for subpacketTotal := 0; subpacketTotal < p.subpacketCount; { + sp, err := parsePacket(bitReader, depth+1) + log.Printf(log.DIAGNOSTIC, " [S: %d]", subpacketTotal+1) + if err != nil { + return p, fmt.Errorf("%s for subpacket %d of id#%d", err.Error(), subpacketTotal+1, p.id) + } + subpacketTotal++ + p.subValues += sp.value + sp.subValues + p.subVersions += sp.version + sp.subVersions + p.subBitCount += sp.bitCount + sp.subBitCount + } + } else { + lengthBits, err := bitReader.ReadBits(15) + p.bitCount += 15 + if err != nil { + return p, fmt.Errorf("%s for subpacket length", err.Error()) + } + p.subBitCount = bits.ToInt(lengthBits) + log.Printf(log.DIAGNOSTIC, " (length: %d) {", p.subBitCount) + subpacketBits := 0 + for subpacketBits < p.subBitCount { + sp, err := parsePacket(bitReader, depth+1) + log.Printf(log.DIAGNOSTIC, " [S: %d]", p.subpacketCount+1) + if err != nil { + return p, fmt.Errorf("%s for subpacket of id#%d", err.Error(), p.id) + } + subpacketBits += sp.bitCount + sp.subBitCount + p.subValues += sp.value + sp.subValues + p.subVersions += sp.version + sp.subVersions + p.subpacketCount += 1 + sp.subpacketCount + } + if subpacketBits != p.subBitCount { + return p, fmt.Errorf("packet #%d should parse %d bits but parsed %d", p.id, p.subBitCount, subpacketBits) + } + } + } + + if p.pType != 4 { + log.Printf(log.DIAGNOSTIC, "\n%s}", strings.Repeat(" ", 6+depth*2)) + } + return p, nil +} + +type BitReader struct { + file *os.File + values []bool +} + +func NewBitReader(file *os.File) BitReader { + return BitReader{file, []bool{}} +} + +var totalBytesRead = 0 + +func (b *BitReader) ReadBits(num int) ([]bool, error) { + if len(b.values) < num { + // each char is worth 4 bits + byteCount := int(math.Ceil(float64(num-len(b.values)) / 4)) + bytes := make([]byte, byteCount) + b.file.Read(bytes) + log.Printf(log.NEVER, "\n [Reading in %d more byte(s) to fulfill %d bit read]", byteCount, num) + totalBytesRead += byteCount + err := b.AddToValues(bytes) + if err != nil { + return nil, fmt.Errorf("%s while reading %d bits", err.Error(), num) + } + } + + // read the rest from input and set any remainder + bits := b.values[:num] + b.values = b.values[num:] + log.Printf(log.NEVER, " \n [Read %d bits from reader and left %d remainder values]", len(bits), len(b.values)) + + return bits, nil +} + +func (b *BitReader) ReadBit() (bool, error) { + bits, err := b.ReadBits(1) + if err == nil { + return bits[0], nil + } else { + return false, err + } +} + +func (b *BitReader) AddToValues(bytes []byte) error { + for bIdx, byte := range bytes { + newBits, err := bits.HexByteToBits(byte) + if err != nil { + return fmt.Errorf("%s @ byte #%d", err.Error(), totalBytesRead-len(bytes)+bIdx+1) + } + b.values = append(b.values, newBits...) + } + return nil +} diff --git a/day-16/main_test.go b/day-16/main_test.go new file mode 100644 index 0000000..17336fa --- /dev/null +++ b/day-16/main_test.go @@ -0,0 +1,11 @@ +package main + +import ( + "AoC2021/utils/assert" + "testing" +) + +func TestDay16(t *testing.T) { + assert.IsTrue(t, true, "cool") + assert.EqualsStr(t, "foo", "bar", "baz") +} diff --git a/day-16/output.txt b/day-16/output.txt new file mode 100644 index 0000000..534af3b --- /dev/null +++ b/day-16/output.txt @@ -0,0 +1 @@ +11000011000111000101001010000001001100100110000100011010110011001100000001010011110011001000110011100101101000010101010100110101001110100100100111001001010000010011000010011100011010001001010110001111010110010101000100101001010011101010010111001100101100000010000100011000100001010110000100011000100010000001000000000001010010100001101011000010101100100001000101010011001011101100001110011000110000000000000101010100010000010001010000101100110010001011100000010001010111000010001111001111110000110101110010111110111011110011010111001001010010100100111010011011000100000000000100010110101001000101111111100101001011000110011001011010110011000001001101011001110001101000100110110010111110011001011001011001111101010010010100010001000000110011010110010001000101000110010110000001000110000001010011100000100111001110000010001100111000100110001000010001010111100101001111110010101011101111010111101110011011101100000000000110000100011011110011000011100111001111111110100000110010000011100100100001000101101010100100111100101001011110000100011011010010101000111101001001010111100010011010100110010101011110111110110110101100010001010110001111001111000001101111000101101011100010000001010010110010100000010011100101000000011111011001100001000110010100010000001100000100011010010101100110101111111100101111100110010000000010000001011111111011001111000000110010110000010001000111000110001101101001111010100110011001010100010110001001100101010000100110101100111010101111000111101011011011000000101000110001000110110110111101000001001001011001110011100000001011001011010110110011010101010000100100111111101000101100000110101001010111110110111000001001100000001010001000010001100110010000100000010010101110000001000100010011001010110001010100110011001101000000110000010001000101000011111000011100110001010000100000001100000000000011010001000000000100010100111001000110000000111001010100100101011010000001001011110011100111101100110010101010100001010000110010111011010101010001100010000001001110000101100111110011111010011100001110101001000000011000100100110100111110011111000000100101000100011010101001001111000010011111010100110001110001010000000010100110011010011110110001010000010101101010110001011100100011101000010111001001010111100010001100000101100110011100010011000100010011000001001000011001110000011010111100010001010000110101111000001100110001100101100010000101000011110010111001100000000011000100000100110001000110000101111001100001110000000100000100100001100111100000001010010110000100110000000100010110000101000001001010001000110010010010000110100000101010000001000100110010101100100101011000001111110001100011000100010001010000000011001010110100001000001000111001010011110001010101101000110001000110010110111001000101001010001100000011100101000100010010011010110010100111100001101110101000000100110000001101011110100001011000010101100011110000101000000100111011001110001010110001000001001010101001100010000001000000100001111001100010000101000010010000010001000101000000111000111010110001011110100010100100100000010001010000000100110010110011010010100000100001001010010000010010100101000001001011001001001111000010010101001011010000000101100101011011100000000001000100010011101111000100100101011110110010011100110000110101110011100110100001101010011010100100000010010011010110111000101010011100111111111110000010101001011000100110111001010101100100000100010001010001000101001010010010111011101101010100100110000100001001010001000110110011011011000101111001101110000000010110100010011001110001011011010011000101000110010011010010110101010111101111001101010100101101001000011010001000000101000010110011000001000101001010010100110110111011000000111110101110110010111100101100110011100101011010111010111100100110000100111000011000111000010001011001011100001000010010100000100011111000010001011000001000000100100011011000010001010110101000000100100011010000010100100011001011000100100001110001001100000100001100110010010001000111100010011010100001000101010011000100011011100110001110000100010101100000010001100100101110111000010001010010110001000101101001000100110001000101001011000100010100001000010001000100011010111100010001000100100101100100000000001100110000001000010001011010001000000100100001100110000101001000100110101000000010001010000001000011000001010111100000110011000100101100000001000101100110000110111100010100111000000011000001000100011000001011001111001101100100100100100001010010011111000011000110100100011110100110000001001101010000100101100110001011100001000111000100000100010001001000011010100010111001001011110011001000101000100000010000000000100001101000111111010000000000100101100110100110011100010100000000000100010101111000000101111010000001001100000000001000101010010010111011100110010110001001010011010010100100111000111100000100110101111011001000110001101111010110010101001111000011010100111000101100000000000011111010010010101100000010000110110000011010010101101111000010011100010111001011010101010011000100011100100100111000000100100001001010011110010110011001111001011011100101101110110001101111100010110101010101011000110000010010010010010101010110111100000100010100100000010001001000011100100010011100001000010110001101000001100000010000010100101000111000000010110001000011000000010001010100111100000100100101100001101111001101010011111111110000010101001100100011000100110001000000010111001111010100111011110000111100000000010001010101100101000101100110010101010001101000111010100100010001010000110101111000010001100001001100001101010100010100001001111110111000111000011000000100100010001011100001 \ No newline at end of file diff --git a/utils/bits/bits.go b/utils/bits/bits.go index b5b0971..6e0fbc5 100644 --- a/utils/bits/bits.go +++ b/utils/bits/bits.go @@ -1,5 +1,7 @@ package bits +import "fmt" + func GetBitCount(val int) int { count := 0 for val != 0 { @@ -52,3 +54,66 @@ func BitwiseAndA(a ...int) int { func ByBitCount(size int) func(int) bool { return func(val int) bool { return GetBitCount(val) == size } } + +func ToInt(bits []bool) int { + intVal := 0 + for idx := 0; idx < len(bits); idx++ { + intVal = intVal << 1 + if bits[idx] { + intVal += 1 + } + } + return intVal +} + +func ToStr(bits []bool) string { + str := "" + for idx := 0; idx < len(bits); idx++ { + if bits[idx] { + str += "1" + } else { + str += "0" + } + } + return str +} + +func HexByteToBits(b byte) ([]bool, error) { + // just do this manually because i can't be arsed + switch b { + case byte('0'): + return []bool{false, false, false, false}, nil + case byte('1'): + return []bool{false, false, false, true}, nil + case byte('2'): + return []bool{false, false, true, false}, nil + case byte('3'): + return []bool{false, false, true, true}, nil + case byte('4'): + return []bool{false, true, false, false}, nil + case byte('5'): + return []bool{false, true, false, true}, nil + case byte('6'): + return []bool{false, true, true, false}, nil + case byte('7'): + return []bool{false, true, true, true}, nil + case byte('8'): + return []bool{true, false, false, false}, nil + case byte('9'): + return []bool{true, false, false, true}, nil + case byte('A'): + return []bool{true, false, true, false}, nil + case byte('B'): + return []bool{true, false, true, true}, nil + case byte('C'): + return []bool{true, true, false, false}, nil + case byte('D'): + return []bool{true, true, false, true}, nil + case byte('E'): + return []bool{true, true, true, false}, nil + case byte('F'): + return []bool{true, true, true, true}, nil + default: + return []bool{}, fmt.Errorf("read invalid byte") + } +} diff --git a/utils/bits/bits_test.go b/utils/bits/bits_test.go index 3e5698c..35791e5 100644 --- a/utils/bits/bits_test.go +++ b/utils/bits/bits_test.go @@ -26,3 +26,37 @@ func TestByBitCount(t *testing.T) { twoBitFilter := ByBitCount(2) assert.IsTrue(t, twoBitFilter(17 /*10001*/), "should count bits") } + +func TestHexByteToBits(t *testing.T) { + cases := []struct { + input rune + output 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"}, + } + + for _, c := range cases { + bits, err := HexByteToBits(byte(c.input)) + if err != nil { + t.Logf("Got error from HexByteToBits: %s", err.Error()) + t.Fail() + } + bitStr := ToStr(bits) + assert.EqualsStr(t, bitStr, c.output, "incorrect value") + } +} diff --git a/utils/files/files.go b/utils/files/files.go new file mode 100644 index 0000000..fbe8d8a --- /dev/null +++ b/utils/files/files.go @@ -0,0 +1,27 @@ +package files + +import ( + "errors" + "fmt" + "os" +) + +func CreateOrReplace(fileName string) (*os.File, error) { + var output *os.File + if _, err := os.Stat(fileName); errors.Is(err, os.ErrNotExist) { + output, err = os.Create(fileName) + if err != nil { + return nil, fmt.Errorf("Error creating file %s", fileName) + } + } else { + err = os.Truncate(fileName, 0) + if err != nil { + return nil, fmt.Errorf("Error truncating file %s", fileName) + } + output, err = os.Open(fileName) + if err != nil { + return nil, fmt.Errorf("Error opening file %s", fileName) + } + } + return output, nil +} diff --git a/utils/log/log.go b/utils/log/log.go index 4a55ce8..5f1a926 100644 --- a/utils/log/log.go +++ b/utils/log/log.go @@ -1,12 +1,16 @@ package log -import "fmt" +import ( + "fmt" + "strconv" +) const ( NONE = 0 NORMAL = 1 DEBUG = 2 DIAGNOSTIC = 3 + NEVER = 99 ) const ( @@ -24,6 +28,31 @@ func SetLogLevel(level int) { } } +func SetLogLevelFromArgs(argMap map[string]string) { + logLevelStr, found := argMap["log"] + if found { + logLevel, err := NORMAL, error(nil) + + switch logLevelStr { + case "NONE", "None", "none": + logLevel = NONE + case "NORMAL", "Normal", "normal": + logLevel = NORMAL + case "DEBUG", "Debug", "debug": + logLevel = DEBUG + case "DIAG", "DIAGNOSTIC", "Diag", "Diagnostic", "diag", "diagnostic": + logLevel = DIAGNOSTIC + default: + logLevel, err = strconv.Atoi(logLevelStr) + } + + if err != nil { + panic("Error when parsing log level") + } + SetLogLevel(logLevel) + } +} + func Print(minLevel int, a ...interface{}) (int, error) { if LOG_LEVEL >= minLevel { return fmt.Print(a...) diff --git a/utils/numbers/numbers.go b/utils/numbers/numbers.go index c7f9a12..99dddbf 100644 --- a/utils/numbers/numbers.go +++ b/utils/numbers/numbers.go @@ -9,3 +9,21 @@ func Min(i1 int, a ...int) int { } return min } + +func Max(i1 int, a ...int) int { + max := i1 + for _, i := range a { + if i > max { + max = i + } + } + return max +} + +func BoolToInt(b bool) int { + if b { + return 1 + } else { + return 0 + } +}