code.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. "strings"
  8. )
  9. type point struct {
  10. x int
  11. y int
  12. }
  13. func readPoints(line string) []point {
  14. parts := strings.Split(line, " -> ")
  15. if len(parts) == 0 {
  16. log.Fatal("Wrong input:", line)
  17. }
  18. var points []point
  19. for i := range parts {
  20. var current point
  21. n, err := fmt.Sscanf(parts[i], "%d,%d", &current.x, &current.y)
  22. if n != 2 || err != nil {
  23. log.Fatal("Can't parse", line, err)
  24. }
  25. points = append(points, current)
  26. }
  27. return points
  28. }
  29. func readInput(file *os.File) [][]point {
  30. scanner := bufio.NewScanner(file)
  31. var points [][]point
  32. for scanner.Scan() {
  33. line := scanner.Text()
  34. if line == "" {
  35. continue
  36. }
  37. points = append(points, readPoints(line))
  38. }
  39. return points
  40. }
  41. func drawHorizontal(first point, second point, rocks map[point]bool) {
  42. start := first.x
  43. end := second.x
  44. if start > second.x {
  45. start = second.x
  46. end = first.x
  47. }
  48. for i := start + 1; i < end; i++ {
  49. rocks[point{i, first.y}] = true
  50. }
  51. }
  52. func drawVertical(first point, second point, rocks map[point]bool) {
  53. start := first.y
  54. end := second.y
  55. if start > second.y {
  56. start = second.y
  57. end = first.y
  58. }
  59. for i := start + 1; i < end; i++ {
  60. rocks[point{first.x, i}] = true
  61. }
  62. }
  63. func drawPath(first point, second point, rocks map[point]bool) {
  64. if first.x == second.x {
  65. drawVertical(first, second, rocks)
  66. } else {
  67. drawHorizontal(first, second, rocks)
  68. }
  69. }
  70. func buildRocks(points [][]point) (map[point]bool, int) {
  71. rocks := make(map[point]bool)
  72. bottom := 0
  73. for i := range points {
  74. edge := len(points[i])
  75. prev := points[i][0]
  76. rocks[prev] = true
  77. if prev.y > bottom {
  78. bottom = prev.y
  79. }
  80. for j := 1; j < edge; j++ {
  81. current := points[i][j]
  82. rocks[current] = true
  83. if current.y > bottom {
  84. bottom = current.y
  85. }
  86. drawPath(prev, current, rocks)
  87. prev = current
  88. }
  89. }
  90. return rocks, bottom
  91. }
  92. func moveUnit(unit point, rocks map[point]bool) point {
  93. current := point{unit.x, unit.y + 1}
  94. if !rocks[current] {
  95. return current
  96. }
  97. current.x--
  98. if !rocks[current] {
  99. return current
  100. }
  101. current.x += 2
  102. if !rocks[current] {
  103. return current
  104. }
  105. return unit
  106. }
  107. func fall(unit point, rocks map[point]bool, bottom int) bool {
  108. for {
  109. current := moveUnit(unit, rocks)
  110. if current.x == unit.x && current.y == unit.y {
  111. rocks[current] = true
  112. break
  113. }
  114. if current.y > bottom {
  115. return true
  116. }
  117. unit = current
  118. }
  119. return false
  120. }
  121. func fall2(unit point, rocks map[point]bool, bottom int) bool {
  122. for {
  123. current := moveUnit(unit, rocks)
  124. if current.x == 500 && current.y == 0 {
  125. rocks[current] = true
  126. return true
  127. }
  128. if current.x == unit.x && current.y == unit.y || current.y == bottom {
  129. rocks[current] = true
  130. break
  131. }
  132. unit = current
  133. }
  134. return false
  135. }
  136. func sandstorm(rocks map[point]bool, bottom int, fall func(point, map[point]bool, int) bool) int {
  137. initialRocks := len(rocks)
  138. currentRocks := initialRocks
  139. for {
  140. if fall(point{500, 0}, rocks, bottom) {
  141. break
  142. }
  143. if len(rocks) == currentRocks {
  144. break
  145. }
  146. currentRocks = len(rocks)
  147. }
  148. return len(rocks) - initialRocks
  149. }
  150. func main() {
  151. if len(os.Args) < 2 {
  152. log.Fatal("You need to specify a file!")
  153. }
  154. filePath := os.Args[1]
  155. file, err := os.Open(filePath)
  156. if err != nil {
  157. log.Fatalf("Failed to open %s!\n", filePath)
  158. }
  159. points := readInput(file)
  160. rocks, bottom := buildRocks(points)
  161. fmt.Println("Part1:", sandstorm(rocks, bottom, fall))
  162. rocks, bottom = buildRocks(points)
  163. fmt.Println("Part2:", sandstorm(rocks, bottom+1, fall2))
  164. }