From 32827d0602e75c4ca673d021d493b5a1280e3847 Mon Sep 17 00:00:00 2001 From: Russell Champoux Date: Fri, 3 Dec 2021 14:22:45 -0500 Subject: [PATCH] day 3 --- day-3/main.go | 205 ++++++++++++++++++++++++++++++++++++++++++ day-3/main_test.go | 11 +++ main_test.go.template | 2 +- 3 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 day-3/main.go create mode 100644 day-3/main_test.go diff --git a/day-3/main.go b/day-3/main.go new file mode 100644 index 0000000..39aaca7 --- /dev/null +++ b/day-3/main.go @@ -0,0 +1,205 @@ +/* Day 3: Binary Diagnostic */ +package main + +import ( + "AoC2021/utils/timer" + "bufio" + "fmt" + "io" + "os" + "strconv" +) + +const BITLENGTH = 12 + +func check(e error) { + if e != nil { + fmt.Println("Oh snap!") + panic(e) + } +} + +func main() { + fmt.Println("--- Day 3: Binary Diagnostic ---") + timer.Start() + part1() + timer.Tick() + fmt.Println() + + part2() + timer.Tick() + fmt.Println() +} + +func bodgeData(bits [BITLENGTH]int, fn func(int) bool) int { + value := 0 + for idx := 0; idx < BITLENGTH; idx++ { + //fmt.Print("bodgeData: comparing ", bits[idx]) + if fn(bits[idx]) { + value += 1 << (BITLENGTH - idx - 1) + //fmt.Print(": value is now ", value) + } + //fmt.Println() + } + + return value +} + +func part1() { + fmt.Println("* Part 1 *") + fmt.Println(" Goal: Use the binary numbers in your diagnostic report to calculate the gamma rate and epsilon rate, then multiply them together") + fmt.Println(" Answer: What is the power consumption of the submarine?") + + input, err := os.Open("./input.txt") + defer input.Close() + check(err) + + reader := bufio.NewReader(input) + lines := 0 + var bits [BITLENGTH]int + + activeBit := 0 + for { + if c, _, err := reader.ReadRune(); err != nil { + if err == io.EOF { + lines++ + break + } else { + panic(err) + } + } else { + if string(c) == "\n" { + lines++ + activeBit = 0 + continue + } else { + val, err := strconv.Atoi(string(c)) + check(err) + bits[activeBit] += val + activeBit += 1 + } + } + } + + gamma := bodgeData(bits, func(val int) bool { return val > lines/2 }) + epsilon := bodgeData(bits, func(val int) bool { return val < lines/2 }) + + fmt.Printf("Read %d lines from input\n", lines) + fmt.Println("bits:", bits) + fmt.Printf("Gamma: %d, Epsilon: %d, Solution: %d\n", gamma, epsilon, gamma*epsilon) +} + +// compare a byte slice with a filter +func byteMatch(value []byte, filter [BITLENGTH]byte) bool { + if len(value) != len(filter) { + return false + } + for idx, b := range filter { + if b == byte(0) { + // once we hit a part of the filter with empty values, we are good + return true + } + if b != value[idx] { + // if a part of our value every doesn't match the filter, we are bad + return false + } + } + return true +} + +func getBitCount(input *os.File, position int, filter [BITLENGTH]byte) (int, int) { + scanner := bufio.NewScanner(input) + count := 0 + lines := 0 + for scanner.Scan() { + bytes := scanner.Bytes() + if !byteMatch(bytes, filter) { + continue + } + lines += 1 + if len(bytes) < position { + panic("not enough bytes") + } + if bytes[position] == byte('1') { + count += 1 + } + } + + return count, lines +} + +func gitBitValue(input *os.File, filter [BITLENGTH]byte) int { + scanner := bufio.NewScanner(input) + value := 0 + + for scanner.Scan() { + bytes := scanner.Bytes() + if !byteMatch(bytes, filter) { + continue + } + // we got a match! + for idx, b := range bytes { + if b == byte('1') { + value += 1 << (BITLENGTH - idx - 1) + } + } + break + } + return value +} + +func doTheThing(input *os.File, reverse bool) int { + var filter [BITLENGTH]byte + yesByte, noByte := byte('1'), byte('0') + if reverse { + yesByte, noByte = byte('0'), byte('1') + } + + for idx := 0; idx < BITLENGTH; idx++ { + _, err := input.Seek(0, io.SeekStart) + check(err) + + ones, lines := getBitCount(input, idx, filter) + //fmt.Printf("BC@%d: %d (%d lines) using filter [%s]\n", idx+1, ones, lines, filter) + + if lines == 1 { + break + } + if float64(ones) >= float64(lines)/2 { + filter[idx] = yesByte + } else { + filter[idx] = noByte + } + } + + _, err := input.Seek(0, io.SeekStart) + check(err) + + // fmt.Printf("=> finding value that matches %s\n", filter) + value := gitBitValue(input, filter) + + return value +} + +func getGeneratorValue(input *os.File) int { + return doTheThing(input, false) +} + +func getScrubberValue(input *os.File) int { + return doTheThing(input, true) +} + +func part2() { + fmt.Println("* Part 2 *") + fmt.Println(" Goal: Use the binary numbers in your diagnostic report to calculate the oxygen generator rating and CO2 scrubber rating, then multiply them together") + fmt.Println(" Answer: What is the life support rating of the submarine?") + + input, err := os.Open("./input.txt") + defer input.Close() + check(err) + + generator := getGeneratorValue(input) + scrubber := getScrubberValue(input) + + fmt.Printf("Generator: %d, Scrubber: %d, Solution: %d\n", generator, scrubber, generator*scrubber) +} diff --git a/day-3/main_test.go b/day-3/main_test.go new file mode 100644 index 0000000..96840de --- /dev/null +++ b/day-3/main_test.go @@ -0,0 +1,11 @@ +package main + +import ( + assert "AoC2021/utils/testing" + "testing" +) + +func TestDay3(t *testing.T) { + assert.IsTrue(t, true, "cool") + assert.StrEquals(t, "foo", "bar", "baz") +} diff --git a/main_test.go.template b/main_test.go.template index 8ba5789..62153ec 100644 --- a/main_test.go.template +++ b/main_test.go.template @@ -7,5 +7,5 @@ import ( func TestDay#DAY#(t *testing.T) { assert.IsTrue(t, true, "cool") - assert.StrEquals(t, "foo", "bar", c.msg) + assert.StrEquals(t, "foo", "bar", "baz") }