113 lines
2.3 KiB
Go
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)
|
|
} |