code.go 4.2 KB

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