From 27b95e6717314e40c293caeae445df48a12fff1c Mon Sep 17 00:00:00 2001 From: Katherina Walshe-Grey Date: Tue, 5 Nov 2024 13:31:09 +0000 Subject: [PATCH] Day 3a --- day03/day03.go | 113 ++++++++++++++++++++++++++++++++++++++++++++ day03/day03_test.go | 25 ++++++++++ day03/input.txt | 10 ++++ 3 files changed, 148 insertions(+) create mode 100644 day03/day03.go create mode 100644 day03/day03_test.go create mode 100644 day03/input.txt diff --git a/day03/day03.go b/day03/day03.go new file mode 100644 index 0000000..e0bb49d --- /dev/null +++ b/day03/day03.go @@ -0,0 +1,113 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + + +const DIGITS = "0123456789" +const SPACE = '.' + +type Number struct { + number int + index int + width int +} + + +func isDigit(in rune) bool { + return strings.ContainsRune(DIGITS, in) +} + +func isNotDigit(in rune) bool { + return !isDigit(in) +} + +func isSpace(in rune) bool { + return in == SPACE +} + +func isPart(in rune) bool { + return !isDigit(in) && !isSpace(in) +} + +func hasPartAtPos(line string, at int) bool { + return at >= 0 && at < len(line) && isPart(rune(line[at])) +} + +func hasPartInRange(line string, from int, to int) bool { + return len(line) > from && strings.ContainsFunc(line[max(from, 0):min(to, len(line))], isPart) +} + +func findNumbers(line string) (numbers []Number) { + offset, index := 0, 0 + for { + index = strings.IndexFunc(line[offset:], isDigit) + if index == -1 { + break + } + + width := strings.IndexFunc(line[offset+index:], isNotDigit) + if width == -1 { + width = len(line) - offset - index + } + + numberA := line[offset+index:offset+index+width] + numberI, err := strconv.Atoi(numberA) + if err != nil { + panic(fmt.Sprintf(`day03: could not parse number: "%s"`, numberA)) + } + + numbers = append(numbers, Number{numberI, offset+index, width}) + offset += index + width + } + return +} + +func ScanFile(file *os.File) (int, error) { + scanner := bufio.NewScanner(file) + var sum int + + var prevLine string + var prevNumbers []Number + for scanner.Scan() { + line := scanner.Text() + numbers := findNumbers(line) + + var unmatchedNumbers []Number + for _, v := range numbers { + number, index, width := v.number, v.index, v.width + if hasPartAtPos(line, index - 1) || hasPartAtPos(line, index + width) || hasPartInRange(prevLine, index - 1, index + width + 1) { + sum += number + } else { + unmatchedNumbers = append(unmatchedNumbers, v) + } + } + for _, v := range prevNumbers { + number, index, width := v.number, v.index, v.width + if hasPartInRange(line, index - 1, index + width + 1) { + sum += number + } + } + + prevLine = line + prevNumbers = unmatchedNumbers + } + if err := scanner.Err(); err != nil { + return 0, err + } + return sum, nil +} + +func main() { + sum, err := ScanFile(os.Stdin) + if err != nil { + fmt.Fprintf(os.Stderr, "Invalid input: %s\n", err) + return + } + fmt.Println(sum) +} \ No newline at end of file diff --git a/day03/day03_test.go b/day03/day03_test.go new file mode 100644 index 0000000..3d2f28c --- /dev/null +++ b/day03/day03_test.go @@ -0,0 +1,25 @@ +package main + +import ( + "os" + "testing" +) + +func TestScanFile(t *testing.T) { + inFile := "input.txt" + want := 4361 + + f, err := os.Open(inFile) + if err != nil { + t.Fatal(err) + } + + got, err := ScanFile(f) + if err != nil { + t.Fatal(err) + } + + if got != want { + t.Errorf("ScanFile(%q) == %d, want %d", inFile, got, want) + } +} \ No newline at end of file diff --git a/day03/input.txt b/day03/input.txt new file mode 100644 index 0000000..624ea4f --- /dev/null +++ b/day03/input.txt @@ -0,0 +1,10 @@ +467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598.. \ No newline at end of file