package main import ( "bufio" "fmt" "os" "strconv" "time" ) func mustAtoi(line string) int { i, _ := strconv.Atoi(line) return i } func main() { start := time.Now() partOne() duration := time.Since(start) partTwo() duration2 := time.Since(start) fmt.Printf("p1: %s, p2: %s\n", duration, duration2-duration) } func makeScanner(test bool) *bufio.Scanner { var f *os.File if test { f, _ = os.Open("inputs/testinput") } else { f, _ = os.Open("inputs/input") } reader := bufio.NewReader(f) return bufio.NewScanner(reader) } type schematic map[string]entry type entry struct { num string symb string } func key(row, col int) string { return fmt.Sprintf("%d.%d", row, col) } func isSymbol(s schematic, row, col int) bool { if c, ok := s[key(row, col)]; ok { return c.symb != "" } return false } func partOne() { scanner := makeScanner(false) row := 0 schematic := schematic{} // read the whole thing in for scanner.Scan() { line := scanner.Text() i := struct { tmp []rune col int }{} for col, c := range line { switch c { case '.': if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: string(i.tmp)} i.tmp = []rune{} } continue case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0': if len(i.tmp) == 0 { i.tmp = append(i.tmp, c) i.col = col } else { i.tmp = append(i.tmp, c) } continue default: if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: string(i.tmp)} i.tmp = []rune{} } schematic[key(row, col)] = entry{symb: string(c)} continue } } // check if we have a number still to save but ran out of line if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: string(i.tmp)} i.tmp = []rune{} } row++ } sum := 0 // now, map back over the schematics and find the parts for row := 0; row <= 139; row++ { for col := 0; col <= 139; col++ { if num, ok := schematic[key(row, col)]; ok && num.num != "" { part := mustAtoi(num.num) if isSymbol(schematic, row, col-1) { // look behind sum += part } else if isSymbol(schematic, row, col+len(num.num)) { // look ahead sum += part } else { for i := -1; i < len(num.num)+1; i++ { // check row above and below if isSymbol(schematic, row-1, col+i) { sum += part break } else if isSymbol(schematic, row+1, col+i) { sum += part break } } } } } } fmt.Println(sum) } func partTwo() { scanner := makeScanner(false) row := 0 schematic := schematic{} // read the whole thing in for scanner.Scan() { line := scanner.Text() i := struct { tmp []rune col int }{} for col, c := range line { switch c { case '1', '2', '3', '4', '5', '6', '7', '8', '9', '0': if len(i.tmp) == 0 { i.tmp = append(i.tmp, c) i.col = col } else { i.tmp = append(i.tmp, c) } continue default: if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: string(i.tmp)} i.tmp = []rune{} } if c == '*' { // no longer care about other symbols schematic[key(row, col)] = entry{symb: string(c)} } continue } } // check if we have a number still to save but ran out of line if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: string(i.tmp)} i.tmp = []rune{} } row++ } gears := map[string][]int{} // map back over the schematics and find the gears for row := 0; row <= 139; row++ { for col := 0; col <= 139; col++ { if num, ok := schematic[key(row, col)]; ok && num.num != "" { part := mustAtoi(num.num) if isSymbol(schematic, row, col-1) { if schematic[key(row, col-1)].symb == "*" { gears[key(row, col-1)] = append(gears[key(row, col-1)], part) } } else if isSymbol(schematic, row, col+len(num.num)) { if schematic[key(row, col+len(num.num))].symb == "*" { gears[key(row, col+len(num.num))] = append(gears[key(row, col+len(num.num))], part) } } else { for i := -1; i < len(num.num)+1; i++ { // check row above and below if isSymbol(schematic, row-1, col+i) { if schematic[key(row-1, col+i)].symb == "*" { gears[key(row-1, col+i)] = append(gears[key(row-1, col+i)], part) } break } else if isSymbol(schematic, row+1, col+i) { if schematic[key(row+1, col+i)].symb == "*" { gears[key(row+1, col+i)] = append(gears[key(row+1, col+i)], part) } break } } } } } } sum := 0 for _, g := range gears { if len(g) == 2 { sum += g[0] * g[1] } } fmt.Println(sum) }