Files
aoc2021/nine/vents.go
2021-12-09 19:52:34 +00:00

177 lines
2.8 KiB
Go

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]
}