code.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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, beams []Beam) int {
  144. trackBoard := emptyBoard(height, width)
  145. pastBeams := make(map[Point]bool)
  146. pastBeams[beams[0].pos] = true
  147. for {
  148. if len(beams) == 0 {
  149. break
  150. }
  151. var newBeams []Beam
  152. for i := range beams {
  153. if trackBoard[beams[i].pos.y][beams[i].pos.x] != Mark {
  154. trackBoard[beams[i].pos.y][beams[i].pos.x] = Mark
  155. }
  156. newBeams = append(newBeams, beams[i].move(board, height, width, pastBeams)...)
  157. }
  158. beams = newBeams
  159. }
  160. return count(trackBoard)
  161. }
  162. func part1(board [][]byte) int {
  163. height := len(board)
  164. width := len(board[0])
  165. beams := []Beam{Beam{pos: Point{y: 0, x: 0, direction: East}, wasHere: make(map[Point]bool)}}
  166. return processBeams(board, height, width, beams)
  167. }
  168. func main() {
  169. if len(os.Args) < 2 {
  170. log.Fatal("You need to specify a file!")
  171. }
  172. filePath := os.Args[1]
  173. file, err := os.Open(filePath)
  174. if err != nil {
  175. log.Fatalf("Failed to open %s!\n", filePath)
  176. }
  177. board := readInput(file)
  178. fmt.Println("Part1:", part1(board))
  179. }