package nine import ( "bufio" "os" "sort" "strconv" "strings" ) type vents struct { floor [][]int counted [][]bool lowPoints *[]basin } type basin struct { lowPoint int size int } func (v *vents) load(filename string) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() v.floor = [][]int{} v.counted = [][]bool{} scanner := bufio.NewScanner(file) for scanner.Scan() { elements := []int{} heights := strings.Split(scanner.Text(), "") counts := make([]bool, len(heights)) for _, h := range heights { v, err := strconv.Atoi(h) if err != nil { return nil } elements = append(elements, v) } v.floor = append(v.floor, elements) v.counted = append(v.counted, counts) } return nil } func (v *vents) findLowPoints() []basin { if v.lowPoints != nil { return *v.lowPoints } lowPoints := []basin{} if len(v.floor) == 0 || len(v.floor[0]) == 0 { return lowPoints } horizontal := len(v.floor[0]) vertical := len(v.floor) for y := 0; y < vertical; y++ { for x := 0; x < horizontal; x++ { minimum := true //Check above if y > 0 { if v.floor[y][x] >= v.floor[y-1][x] { minimum = false } } //Check below if y < vertical-1 { if v.floor[y][x] >= v.floor[y+1][x] { minimum = false } } //Check left if x > 0 { if v.floor[y][x] >= v.floor[y][x-1] { minimum = false } } //Check left if x < horizontal-1 { if v.floor[y][x] >= v.floor[y][x+1] { minimum = false } } if minimum { b := basin{ lowPoint: v.floor[y][x], size: v.basinSize(x, y), } lowPoints = append(lowPoints, b) } } } v.lowPoints = &lowPoints return lowPoints } func (v *vents) basinSize(x int, y int) int { size := 1 v.counted[y][x] = true if v.floor[y][x] == 9 { return 0 } horizontal := len(v.floor[0]) vertical := len(v.floor) //Check above if y > 0 && !v.counted[y-1][x] { if v.floor[y][x] < v.floor[y-1][x] { size += v.basinSize(x, y-1) } } //Check below if y < vertical-1 && !v.counted[y+1][x] { if v.floor[y][x] < v.floor[y+1][x] { size += v.basinSize(x, y+1) } } //Check left if x > 0 && !v.counted[y][x-1] { if v.floor[y][x] < v.floor[y][x-1] { size += v.basinSize(x-1, y) } } //Check left if x < horizontal-1 && !v.counted[y][x+1] { if v.floor[y][x] < v.floor[y][x+1] { size += v.basinSize(x+1, y) } } return size } func (v *vents) sumLowPointRisk() (sum int) { for _, b := range v.findLowPoints() { sum += (b.lowPoint + 1) } return sum } func (v *vents) productLargestBasins() int { sizes := sort.IntSlice{} for _, b := range v.findLowPoints() { sizes = append(sizes, b.size) } sort.Sort(sizes) if len(sizes) < 3 { return 0 } return sizes[len(sizes)-1] * sizes[len(sizes)-2] * sizes[len(sizes)-3] }