code.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. if moves > MaxMoves {
  50. return destinations
  51. }
  52. end := lava.pos.y - MaxMoves
  53. if end < 0 {
  54. end = 0
  55. }
  56. cost := lava.cost
  57. for y := lava.pos.y - 1; y >= end; y-- {
  58. cost += board[y][lava.pos.x]
  59. moves++
  60. if moves > MaxMoves {
  61. break
  62. }
  63. destinations = append(destinations, Destination{pos: Point{y: y, x: lava.pos.x}, moves: moves, cost: cost, direction: North})
  64. }
  65. return destinations
  66. }
  67. func getEast(board [][]int, height int, width int, lava Destination) []Destination {
  68. var destinations []Destination
  69. moves := 0
  70. if lava.direction == East {
  71. moves = lava.moves
  72. }
  73. if moves > MaxMoves {
  74. return destinations
  75. }
  76. end := lava.pos.x + MaxMoves
  77. if end >= width {
  78. end = width - 1
  79. }
  80. cost := lava.cost
  81. for x := lava.pos.x + 1; x <= end; x++ {
  82. cost += board[lava.pos.y][x]
  83. moves++
  84. if moves > MaxMoves {
  85. break
  86. }
  87. destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: East})
  88. if moves > MaxMoves {
  89. break
  90. }
  91. }
  92. return destinations
  93. }
  94. func getSouth(board [][]int, height int, width int, lava Destination) []Destination {
  95. var destinations []Destination
  96. moves := 0
  97. if lava.direction == South {
  98. moves = lava.moves
  99. }
  100. if moves > MaxMoves {
  101. return destinations
  102. }
  103. end := lava.pos.y + MaxMoves
  104. if end >= height {
  105. end = height - 1
  106. }
  107. cost := lava.cost
  108. for y := lava.pos.y + 1; y <= end; y++ {
  109. cost += board[y][lava.pos.x]
  110. moves++
  111. if moves > MaxMoves {
  112. break
  113. }
  114. destinations = append(destinations, Destination{pos: Point{y: y, x: lava.pos.x}, moves: moves, cost: cost, direction: South})
  115. }
  116. return destinations
  117. }
  118. func getWest(board [][]int, height int, width int, lava Destination) []Destination {
  119. var destinations []Destination
  120. moves := 0
  121. if lava.direction == West {
  122. moves = lava.moves
  123. }
  124. if moves > MaxMoves {
  125. return destinations
  126. }
  127. end := lava.pos.x - MaxMoves
  128. if end < 0 {
  129. end = 0
  130. }
  131. cost := lava.cost
  132. for x := lava.pos.x - 1; x >= end; x-- {
  133. cost += board[lava.pos.y][x]
  134. moves++
  135. if moves > MaxMoves {
  136. break
  137. }
  138. destinations = append(destinations, Destination{pos: Point{y: lava.pos.y, x: x}, moves: moves, cost: cost, direction: West})
  139. }
  140. return destinations
  141. }
  142. func getDirections(direction int) []int {
  143. switch direction {
  144. case North:
  145. return []int{East, North, West}
  146. case East:
  147. return []int{East, South, North}
  148. case South:
  149. return []int{East, South, West}
  150. case West:
  151. return []int{South, West, North}
  152. }
  153. return []int{}
  154. }
  155. func getDestinations(board [][]int, height int, width int, lava Destination) []Destination {
  156. var destinations []Destination
  157. directions := getDirections(lava.direction)
  158. for i := range directions {
  159. switch directions[i] {
  160. case North:
  161. destinations = append(destinations, getNorth(board, height, width, lava)...)
  162. case East:
  163. destinations = append(destinations, getEast(board, height, width, lava)...)
  164. case South:
  165. destinations = append(destinations, getSouth(board, height, width, lava)...)
  166. case West:
  167. destinations = append(destinations, getWest(board, height, width, lava)...)
  168. }
  169. }
  170. return destinations
  171. }
  172. func part1(board [][]int) int {
  173. min := 1000000
  174. height := len(board)
  175. width := len(board[0])
  176. goal := Point{y: height - 1, x: width - 1}
  177. explored := make(map[Point]int)
  178. lava := Destination{pos: Point{x: 0, y: 0}, moves: 0, direction: East}
  179. frontier := []Destination{lava}
  180. for {
  181. if len(frontier) == 0 {
  182. break
  183. }
  184. current := frontier[0]
  185. frontier = frontier[1:]
  186. if current.pos == goal {
  187. if min > current.cost {
  188. min = current.cost
  189. }
  190. }
  191. successors := getDestinations(board, height, width, current)
  192. for i := range successors {
  193. newCost := successors[i].cost
  194. value, ok := explored[successors[i].pos]
  195. if !ok || value > newCost {
  196. explored[successors[i].pos] = newCost
  197. frontier = append(frontier, successors[i])
  198. }
  199. }
  200. }
  201. return min
  202. }
  203. func main() {
  204. if len(os.Args) < 2 {
  205. log.Fatal("You need to specify a file!")
  206. }
  207. filePath := os.Args[1]
  208. file, err := os.Open(filePath)
  209. if err != nil {
  210. log.Fatalf("Failed to open %s!\n", filePath)
  211. }
  212. board := readInput(file)
  213. fmt.Println("Part1:", part1(board))
  214. }