code.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. func readInput(file *os.File) [][]byte {
  9. scanner := bufio.NewScanner(file)
  10. var board [][]byte
  11. for scanner.Scan() {
  12. line := scanner.Text()
  13. if line == "" {
  14. break
  15. }
  16. var row []byte
  17. for i := range line {
  18. row = append(row, line[i])
  19. }
  20. board = append(board, row)
  21. }
  22. return board
  23. }
  24. const (
  25. Horizontal = '-'
  26. Vertical = '|'
  27. Slash = '/'
  28. Backslash = '\\'
  29. Empty = '.'
  30. Mark = '#'
  31. )
  32. const (
  33. North = iota
  34. South
  35. East
  36. West
  37. )
  38. type Point struct {
  39. y, x int
  40. direction int
  41. }
  42. type Beam struct {
  43. pos Point
  44. wasHere map[Point]bool
  45. }
  46. func (b *Beam) directions(board [][]byte, height int, width int, pastBeams map[Point]bool) []Beam {
  47. switch board[b.pos.y][b.pos.x] {
  48. case Horizontal:
  49. if b.pos.direction != East && b.pos.direction != West {
  50. var beams []Beam
  51. b.pos.direction = East
  52. if !pastBeams[b.pos] {
  53. pastBeams[b.pos] = true
  54. beams = append(beams, *b)
  55. }
  56. b.pos.direction = West
  57. if !pastBeams[b.pos] {
  58. pastBeams[b.pos] = true
  59. beams = append(beams, *b)
  60. }
  61. return beams
  62. }
  63. case Vertical:
  64. if b.pos.direction != South && b.pos.direction != North {
  65. var beams []Beam
  66. b.pos.direction = South
  67. if !pastBeams[b.pos] {
  68. pastBeams[b.pos] = true
  69. beams = append(beams, *b)
  70. }
  71. b.pos.direction = North
  72. if !pastBeams[b.pos] {
  73. pastBeams[b.pos] = true
  74. beams = append(beams, *b)
  75. }
  76. return beams
  77. }
  78. case Slash:
  79. switch b.pos.direction {
  80. case North:
  81. b.pos.direction = East
  82. case South:
  83. b.pos.direction = West
  84. case East:
  85. b.pos.direction = North
  86. case West:
  87. b.pos.direction = South
  88. }
  89. case Backslash:
  90. switch b.pos.direction {
  91. case North:
  92. b.pos.direction = West
  93. case South:
  94. b.pos.direction = East
  95. case East:
  96. b.pos.direction = South
  97. case West:
  98. b.pos.direction = North
  99. }
  100. }
  101. return []Beam{*b}
  102. }
  103. func (b *Beam) move(board [][]byte, height int, width int, pastBeams map[Point]bool) []Beam {
  104. b.wasHere[b.pos] = true
  105. var beams []Beam
  106. directions := b.directions(board, height, width, pastBeams)
  107. for i := range directions {
  108. switch directions[i].pos.direction {
  109. case North:
  110. directions[i].pos.y--
  111. case South:
  112. directions[i].pos.y++
  113. case East:
  114. directions[i].pos.x++
  115. case West:
  116. directions[i].pos.x--
  117. }
  118. if directions[i].wasHere[directions[i].pos] || directions[i].pos.x < 0 || directions[i].pos.x >= width || directions[i].pos.y < 0 || directions[i].pos.y >= height {
  119. continue
  120. }
  121. beams = append(beams, directions[i])
  122. }
  123. return beams
  124. }
  125. func emptyBoard(height int, width int) [][]byte {
  126. var board [][]byte
  127. for i := 0; i < height; i++ {
  128. board = append(board, make([]byte, width))
  129. }
  130. return board
  131. }
  132. func count(board [][]byte) int {
  133. var result int
  134. for y := range board {
  135. for x := range board[y] {
  136. if board[y][x] == Mark {
  137. result++
  138. }
  139. }
  140. }
  141. return result
  142. }
  143. func processBeams(board [][]byte, height int, width int, beam Beam) int {
  144. trackBoard := emptyBoard(height, width)
  145. pastBeams := make(map[Point]bool)
  146. beams := []Beam{beam}
  147. for {
  148. if len(beams) == 0 {
  149. break
  150. }
  151. var newBeams []Beam
  152. for i := range beams {
  153. pastBeams[beams[i].pos] = true
  154. if trackBoard[beams[i].pos.y][beams[i].pos.x] != Mark {
  155. trackBoard[beams[i].pos.y][beams[i].pos.x] = Mark
  156. }
  157. newBeams = append(newBeams, beams[i].move(board, height, width, pastBeams)...)
  158. }
  159. beams = newBeams
  160. }
  161. return count(trackBoard)
  162. }
  163. func part1(board [][]byte) int {
  164. height := len(board)
  165. width := len(board[0])
  166. return processBeams(board, height, width, Beam{pos: Point{y: 0, x: 0, direction: East}, wasHere: make(map[Point]bool)})
  167. }
  168. func part2(board [][]byte) int {
  169. height := len(board)
  170. bottomEdge := height - 1
  171. width := len(board[0])
  172. rightEdge := width - 1
  173. max := 0
  174. var beams []Beam
  175. for x := 0; x < width; x++ {
  176. beams = append(beams, Beam{pos: Point{y: 0, x: x, direction: South}, wasHere: make(map[Point]bool)})
  177. if x == 0 {
  178. beams = append(beams, Beam{pos: Point{y: 0, x: x, direction: East}, wasHere: make(map[Point]bool)})
  179. }
  180. if x == rightEdge {
  181. beams = append(beams, Beam{pos: Point{y: 0, x: x, direction: West}, wasHere: make(map[Point]bool)})
  182. }
  183. }
  184. for x := 0; x < width; x++ {
  185. beams = append(beams, Beam{pos: Point{y: bottomEdge, x: x, direction: North}, wasHere: make(map[Point]bool)})
  186. if x == 0 {
  187. beams = append(beams, Beam{pos: Point{y: bottomEdge, x: x, direction: East}, wasHere: make(map[Point]bool)})
  188. }
  189. if x == rightEdge {
  190. beams = append(beams, Beam{pos: Point{y: bottomEdge, x: x, direction: West}, wasHere: make(map[Point]bool)})
  191. }
  192. }
  193. for y := 0; y < height; y++ {
  194. beams = append(beams, Beam{pos: Point{y: y, x: 0, direction: East}, wasHere: make(map[Point]bool)})
  195. if y == 0 {
  196. beams = append(beams, Beam{pos: Point{y: y, x: 0, direction: South}, wasHere: make(map[Point]bool)})
  197. }
  198. if y == bottomEdge {
  199. beams = append(beams, Beam{pos: Point{y: y, x: 0, direction: North}, wasHere: make(map[Point]bool)})
  200. }
  201. }
  202. for y := 0; y < height; y++ {
  203. beams = append(beams, Beam{pos: Point{y: y, x: rightEdge, direction: West}, wasHere: make(map[Point]bool)})
  204. if y == 0 {
  205. beams = append(beams, Beam{pos: Point{y: y, x: rightEdge, direction: South}, wasHere: make(map[Point]bool)})
  206. }
  207. if y == bottomEdge {
  208. beams = append(beams, Beam{pos: Point{y: y, x: rightEdge, direction: North}, wasHere: make(map[Point]bool)})
  209. }
  210. }
  211. for i := range beams {
  212. energy := processBeams(board, height, width, beams[i])
  213. if energy > max {
  214. max = energy
  215. }
  216. }
  217. return max
  218. }
  219. func main() {
  220. if len(os.Args) < 2 {
  221. log.Fatal("You need to specify a file!")
  222. }
  223. filePath := os.Args[1]
  224. file, err := os.Open(filePath)
  225. if err != nil {
  226. log.Fatalf("Failed to open %s!\n", filePath)
  227. }
  228. board := readInput(file)
  229. fmt.Println("Part1:", part1(board))
  230. fmt.Println("Part2:", part2(board))
  231. }