aoc2023/day03/day03.go
2024-11-05 13:31:09 +00:00

113 lines
2.3 KiB
Go

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)
}