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