package main import ( "fmt" "strconv" "unicode" ) var symbols = [][]bool{} // enginePart represents a single engine part type enginePart struct { digits []digit } // realValue returns the numeric value of all digits combined func (e *enginePart) realValue() (int, error) { var numStr string for _, digit := range e.digits { numStr += fmt.Sprintf("%d", digit.raw) } n, err := strconv.Atoi(numStr) if err != nil { return 0, err } return n, nil } // digit represents a single digit in an engine part type digit struct { row int col int raw int pass bool } // checkDigit checks if any neighbouring characters are true (in symbol matrix) func checkDigit(d digit) digit { i := d.col - 1 for ; i < len(symbols[d.row]) && i <= d.col+1; i += 1 { if i < 0 { continue } if d.row != 0 { if symbols[d.row-1][i] { d.pass = true break } } if symbols[d.row][i] { d.pass = true break } if d.row != len(symbols)-1 { if symbols[d.row+1][i] { d.pass = true break } } } return d } // checkDigits runs each digit in an engine part through checkDigit() func checkDigits(i, j int, line string) (int, int, error) { enginePart := enginePart{} for ; j < len(line); j += 1 { if !unicode.IsNumber(rune(line[j])) { break } enginePart.digits = append(enginePart.digits, checkDigit(digit{row: i, col: j, raw: int(line[j]) - '0', pass: false})) } if anyTrue(enginePart.digits) { engineParts[i] = append(engineParts[i], enginePart) n, err := enginePart.realValue() if err != nil { return 0, 0, err } return j, n, nil } return j, 0, nil } // symbolToBool generates a boolean matrix that represents the placement of symbols func symbolToBool(line string) []bool { bool_arr := []bool{} for _, c := range line { bool_arr = append(bool_arr, !(unicode.IsNumber(c) || c == '.')) } return bool_arr } // one deciphers which numbers represent engine parts // it returns the sum of all engine parts func one(lines []string) (int, error) { for _, line := range lines { symbols = append(symbols, symbolToBool(line)) } engineParts = make([][]enginePart, len(lines)) sum := 0 for i, line := range lines { for j := 0; j < len(line); j += 1 { if unicode.IsNumber(rune(line[j])) { next, n, err := checkDigits(i, j, line) if err != nil { return 0, err } sum += n j = next } } } return sum, nil }