code.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. const (
  9. None = iota
  10. North
  11. South
  12. East
  13. West
  14. )
  15. type Point struct {
  16. y, x int
  17. steps int
  18. direction int
  19. mustGoDown bool
  20. }
  21. func readInput(file *os.File) [][]byte {
  22. scanner := bufio.NewScanner(file)
  23. var board [][]byte
  24. for scanner.Scan() {
  25. line := scanner.Text()
  26. if line == "" {
  27. break
  28. }
  29. var row []byte
  30. for i := range line {
  31. row = append(row, line[i])
  32. }
  33. board = append(board, row)
  34. }
  35. return board
  36. }
  37. const (
  38. Up = '^'
  39. Right = '>'
  40. Down = 'V'
  41. Left = '<'
  42. Rock = '#'
  43. )
  44. func (p *Point) getDestinations(board [][]byte, height int, width int) []Point {
  45. var destinations []Point
  46. p.steps++
  47. if p.mustGoDown && p.y+1 < height && board[p.y+1][p.x] != Rock {
  48. p.y++
  49. p.direction = None
  50. p.mustGoDown = false
  51. destinations = append(destinations, *p)
  52. return destinations
  53. }
  54. switch board[p.y][p.x] {
  55. case Up:
  56. p.direction = North
  57. p.mustGoDown = true
  58. case Down:
  59. p.direction = None
  60. p.mustGoDown = false
  61. case Left:
  62. p.direction = West
  63. p.mustGoDown = true
  64. case Right:
  65. p.direction = East
  66. p.mustGoDown = true
  67. }
  68. if p.direction != None {
  69. x := p.x
  70. y := p.y
  71. switch p.direction {
  72. case North:
  73. y--
  74. case South:
  75. y++
  76. case West:
  77. x--
  78. case East:
  79. x++
  80. }
  81. destinations = append(destinations, Point{y: y, x: x, steps: p.steps})
  82. return destinations
  83. }
  84. if p.y-1 >= 0 && board[p.y-1][p.x] != Rock {
  85. destinations = append(destinations, Point{y: p.y - 1, x: p.x, steps: p.steps})
  86. }
  87. if p.x+1 < width && board[p.y][p.x+1] != Rock {
  88. destinations = append(destinations, Point{y: p.y, x: p.x + 1, steps: p.steps})
  89. }
  90. if p.x-1 >= 0 && board[p.y][p.x-1] != Rock {
  91. destinations = append(destinations, Point{y: p.y, x: p.x - 1, steps: p.steps})
  92. }
  93. if p.y+1 < height && board[p.y+1][p.x] != Rock {
  94. destinations = append(destinations, Point{y: p.y + 1, x: p.x, steps: p.steps})
  95. }
  96. return destinations
  97. }
  98. func (p *Point) key() string {
  99. return fmt.Sprintf("%d_%d", p.y, p.x)
  100. }
  101. func calculate(board [][]byte) int {
  102. height := len(board)
  103. width := len(board[0])
  104. start := Point{y: 0, x: 1}
  105. goal := Point{y: height - 1, x: width - 2}
  106. visited := make(map[string]int)
  107. visited[start.key()] = start.steps
  108. frontier := []Point{start}
  109. var max int
  110. for {
  111. if len(frontier) == 0 {
  112. break
  113. }
  114. current := frontier[0]
  115. frontier = frontier[1:]
  116. if current.x == goal.x && current.y == goal.y {
  117. if max < current.steps {
  118. max = current.steps
  119. }
  120. continue
  121. }
  122. successors := current.getDestinations(board, height, width)
  123. for i := range successors {
  124. _, ok := visited[successors[i].key()]
  125. if !ok {
  126. visited[successors[i].key()] = successors[i].steps
  127. frontier = append(frontier, successors[i])
  128. }
  129. }
  130. }
  131. return max
  132. }
  133. func main() {
  134. if len(os.Args) < 2 {
  135. log.Fatal("You need to specify a file!")
  136. }
  137. filePath := os.Args[1]
  138. file, err := os.Open(filePath)
  139. if err != nil {
  140. log.Fatalf("Failed to open %s!\n", filePath)
  141. }
  142. board := readInput(file)
  143. fmt.Println("Part1:", calculate(board))
  144. }