package main import ( "bufio" "fmt" "os" "strconv" "time" ) func mustAtoi(line []rune) int { i, _ := strconv.Atoi(string(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 []rune symb rune } type tmp struct { tmp []rune col int } 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 != 0 } return false } func partOne() { scanner := makeScanner(false) row := 0 schematic := schematic{} // read the whole thing in for scanner.Scan() { line := scanner.Text() i := tmp{} 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) } default: if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: i.tmp} i.tmp = []rune{} } if c != '.' { schematic[key(row, col)] = entry{symb: c} } } } // 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: i.tmp} } 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 entry, ok := schematic[key(row, col)]; ok && entry.symb == 0 { part := mustAtoi(entry.num) if isSymbol(schematic, row, col-1) { // look behind sum += part } if isSymbol(schematic, row, col+len(entry.num)) { // look ahead sum += part } for i := -1; i < len(entry.num)+1; i++ { // check row above and below if isSymbol(schematic, row-1, col+i) { sum += part } if isSymbol(schematic, row+1, col+i) { sum += part } } } } } fmt.Println(sum) } func partTwo() { scanner := makeScanner(false) row := 0 schematic := schematic{} // read the whole thing in for scanner.Scan() { line := scanner.Text() i := tmp{} 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) } default: if len(i.tmp) != 0 { schematic[key(row, i.col)] = entry{num: i.tmp} i.tmp = []rune{} } if c == '*' { // no longer care about other symbols schematic[key(row, col)] = entry{symb: c} } } } // 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: i.tmp} } row++ } gears := map[string]int{} sum := 0 // map back over the schematics and find the gears. // there are only ever 1 or 2 parts attached to a gear. for row := 0; row <= 139; row++ { for col := 0; col <= 139; col++ { if entry, ok := schematic[key(row, col)]; ok && entry.symb == 0 { offset := len(entry.num) part := mustAtoi(entry.num) if isSymbol(schematic, row, col-1) { k := key(row, col-1) if gears[k] != 0 { sum += gears[k] * part } else { gears[k] = part } } if isSymbol(schematic, row, col+offset) { k := key(row, col+offset) if gears[k] != 0 { sum += gears[k] * part } else { gears[k] = part } } for i := -1; i < offset+1; i++ { if isSymbol(schematic, row-1, col+i) { k := key(row-1, col+i) if gears[k] != 0 { sum += gears[k] * part } else { gears[k] = part } } if isSymbol(schematic, row+1, col+i) { k := key(row+1, col+i) if gears[k] != 0 { sum += gears[k] * part } else { gears[k] = part } } } } } } fmt.Println(sum) }