code.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "log"
  6. "os"
  7. "sort"
  8. "strconv"
  9. "strings"
  10. )
  11. func readInput(file string) [][]int {
  12. content, err := ioutil.ReadFile(file)
  13. if err != nil {
  14. log.Fatal(err)
  15. }
  16. lines := strings.Split(string(content), "\n")
  17. var input [][]int
  18. for _, line := range lines {
  19. if line == "" {
  20. continue
  21. }
  22. var numbers []int
  23. for _, letter := range line {
  24. if number, err := strconv.Atoi(string(letter)); err == nil {
  25. numbers = append(numbers, number)
  26. } else {
  27. log.Fatal(err)
  28. }
  29. }
  30. input = append(input, numbers)
  31. }
  32. return input
  33. }
  34. func hasSmallerNeighbors(input [][]int, x, y int) bool {
  35. if x-1 >= 0 && input[x-1][y] <= input[x][y] {
  36. return true
  37. }
  38. if x+1 < len(input) && input[x+1][y] <= input[x][y] {
  39. return true
  40. }
  41. if y-1 >= 0 && input[x][y-1] <= input[x][y] {
  42. return true
  43. }
  44. if y+1 < len(input[0]) && input[x][y+1] <= input[x][y] {
  45. return true
  46. }
  47. return false
  48. }
  49. type point struct {
  50. x, y int
  51. }
  52. func part1(input [][]int) (int, []point) {
  53. var sum int
  54. var lowPoints []point
  55. for x, row := range input {
  56. for y, value := range row {
  57. if !hasSmallerNeighbors(input, x, y) {
  58. sum += value + 1
  59. lowPoints = append(lowPoints, point{x, y})
  60. }
  61. }
  62. }
  63. return sum, lowPoints
  64. }
  65. func getNeighbors(input [][]int, x, y int) []point {
  66. var neighbors []point
  67. if x-1 >= 0 && input[x-1][y] != 9 {
  68. neighbors = append(neighbors, point{x - 1, y})
  69. }
  70. if x+1 < len(input) && input[x+1][y] != 9 {
  71. neighbors = append(neighbors, point{x + 1, y})
  72. }
  73. if y-1 >= 0 && input[x][y-1] != 9 {
  74. neighbors = append(neighbors, point{x, y - 1})
  75. }
  76. if y+1 < len(input[0]) && input[x][y+1] != 9 {
  77. neighbors = append(neighbors, point{x, y + 1})
  78. }
  79. return neighbors
  80. }
  81. func getBasin(input [][]int, x int, y int) []point {
  82. var basin []point
  83. neighbors := getNeighbors(input, x, y)
  84. checked := make(map[point]bool)
  85. for {
  86. if len(neighbors) == 0 {
  87. break
  88. }
  89. var next []point
  90. for _, neighbor := range neighbors {
  91. if checked[neighbor] {
  92. continue
  93. }
  94. checked[neighbor] = true
  95. basin = append(basin, neighbor)
  96. next = append(next, getNeighbors(input, neighbor.x, neighbor.y)...)
  97. }
  98. neighbors = next
  99. }
  100. return basin
  101. }
  102. func getBasins(input [][]int, lowPoints []point) [][]point {
  103. var basins [][]point
  104. for _, lowPoint := range lowPoints {
  105. basin := getBasin(input, lowPoint.x, lowPoint.y)
  106. basins = append(basins, basin)
  107. }
  108. return basins
  109. }
  110. func part2(input [][]int, lowPoints []point) int {
  111. basins := getBasins(input, lowPoints)
  112. sort.Slice(basins, func(i, j int) bool {
  113. return len(basins[i]) > len(basins[j])
  114. })
  115. result := 1
  116. for i := 0; i < 3; i++ {
  117. result *= len(basins[i])
  118. }
  119. return result
  120. }
  121. func main() {
  122. if len(os.Args) < 2 {
  123. log.Fatal("Please provide a file name as argument")
  124. }
  125. input := readInput(os.Args[1])
  126. sum, lowPoints := part1(input)
  127. fmt.Println("Part 1:", sum)
  128. fmt.Println("Part 2:", part2(input, lowPoints))
  129. }