code.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. const (
  9. Diff = 48
  10. MaxMoves = 3
  11. )
  12. func readInput(file *os.File) [][]int {
  13. scanner := bufio.NewScanner(file)
  14. var board [][]int
  15. for scanner.Scan() {
  16. line := scanner.Text()
  17. if line == "" {
  18. break
  19. }
  20. var row []int
  21. for i := range line {
  22. row = append(row, int(line[i]-Diff))
  23. }
  24. board = append(board, row)
  25. }
  26. return board
  27. }
  28. const (
  29. North = iota
  30. East
  31. South
  32. West
  33. )
  34. type Point struct {
  35. y, x int
  36. }
  37. type Destination struct {
  38. pos Point
  39. cost int
  40. moves int
  41. direction int
  42. }
  43. func getNorth(board [][]int, height int, width int, lava Destination) []Destination {
  44. var destinations []Destination
  45. moves := 0
  46. if lava.direction == North {
  47. moves = lava.moves
  48. }
  49. end := lava.pos.y - MaxMoves
  50. if end < 0 {
  51. end = 0
  52. }
  53. cost := lava.cost
  54. for y := lava.pos.y - 1; y >= end; y-- {
  55. cost += board[y][lava.pos.x]
  56. moves++
  57. if moves > MaxMoves {
  58. break
  59. }
  60. destinations = append(destinations, Destination{pos: Point{y: y, x: lava.pos.x}, moves: moves, cost: cost, direction: North})
  61. }
  62. return destinations
  63. }
  64. func getEast(board [][]int, height int, width int, lava Destination) []Destination {
  65. var destinations []Destination
  66. moves := 0
  67. if lava.direction == East {
  68. moves = lava.moves
  69. }
  70. end := lava.pos.x + MaxMoves
  71. if end >= width {
  72. end = width - 1
  73. }
  74. cost := lava.cost
  75. for x := lava.pos.x + 1; x <= end; x++ {
  76. cost += board[lava.pos.y][x]
  77. moves++
  78. if moves > MaxMoves {
  79. break
  80. }
  81. destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: East})
  82. if moves > MaxMoves {
  83. break
  84. }
  85. }
  86. return destinations
  87. }
  88. func getSouth(board [][]int, height int, width int, lava Destination) []Destination {
  89. var destinations []Destination
  90. moves := 0
  91. if lava.direction == South {
  92. moves = lava.moves
  93. }
  94. end := lava.pos.y + MaxMoves
  95. if end >= height {
  96. end = height - 1
  97. }
  98. cost := lava.cost
  99. for y := lava.pos.y + 1; y <= end; y++ {
  100. cost += board[y][lava.pos.x]
  101. moves++
  102. if moves > MaxMoves {
  103. break
  104. }
  105. destinations = append(destinations, Destination{pos: Point{y: y, x: lava.pos.x}, moves: moves, cost: cost, direction: South})
  106. }
  107. return destinations
  108. }
  109. func getWest(board [][]int, height int, width int, lava Destination) []Destination {
  110. var destinations []Destination
  111. moves := 0
  112. if lava.direction == West {
  113. moves = lava.moves
  114. }
  115. end := lava.pos.x - MaxMoves
  116. if end < 0 {
  117. end = 0
  118. }
  119. cost := lava.cost
  120. for x := lava.pos.x - 1; x >= end; x-- {
  121. cost += board[lava.pos.y][x]
  122. moves++
  123. if moves > MaxMoves {
  124. break
  125. }
  126. destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: West})
  127. }
  128. return destinations
  129. }
  130. func getDirections(direction int) []int {
  131. switch direction {
  132. case North:
  133. return []int{East, North, West}
  134. case East:
  135. return []int{East, South, North}
  136. case South:
  137. return []int{East, South, West}
  138. case West:
  139. return []int{South, West, North}
  140. }
  141. return []int{}
  142. }
  143. func getDestinations(board [][]int, height int, width int, lava Destination) []Destination {
  144. var destinations []Destination
  145. directions := getDirections(lava.direction)
  146. for i := range directions {
  147. switch directions[i] {
  148. case North:
  149. destinations = append(destinations, getNorth(board, height, width, lava)...)
  150. case East:
  151. destinations = append(destinations, getEast(board, height, width, lava)...)
  152. case South:
  153. destinations = append(destinations, getSouth(board, height, width, lava)...)
  154. case West:
  155. destinations = append(destinations, getWest(board, height, width, lava)...)
  156. }
  157. }
  158. return destinations
  159. }
  160. func part1(board [][]int) int {
  161. min := 1000000
  162. height := len(board)
  163. width := len(board[0])
  164. goal := Point{y: height - 1, x: width - 1}
  165. explored := make(map[Point]int)
  166. lava := Destination{pos: Point{x: 0, y: 0}, moves: 0, direction: East}
  167. frontier := []Destination{lava}
  168. for {
  169. if len(frontier) == 0 {
  170. break
  171. }
  172. current := frontier[0]
  173. frontier = frontier[1:]
  174. if current.pos == goal {
  175. if min > current.cost {
  176. min = current.cost
  177. }
  178. }
  179. successors := getDestinations(board, height, width, current)
  180. for i := range successors {
  181. newCost := successors[i].cost
  182. value, ok := explored[successors[i].pos]
  183. if !ok || value > newCost {
  184. explored[successors[i].pos] = newCost
  185. frontier = append(frontier, successors[i])
  186. }
  187. }
  188. }
  189. return min
  190. }
  191. func main() {
  192. if len(os.Args) < 2 {
  193. log.Fatal("You need to specify a file!")
  194. }
  195. filePath := os.Args[1]
  196. file, err := os.Open(filePath)
  197. if err != nil {
  198. log.Fatalf("Failed to open %s!\n", filePath)
  199. }
  200. board := readInput(file)
  201. fmt.Println("Part1:", part1(board))
  202. }