code.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  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. var beams []Beam
  48. switch board[b.pos.y][b.pos.x] {
  49. case Horizontal:
  50. if b.pos.direction != East && b.pos.direction != West {
  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. b.pos.direction = South
  66. if !pastBeams[b.pos] {
  67. pastBeams[b.pos] = true
  68. beams = append(beams, *b)
  69. }
  70. b.pos.direction = North
  71. if !pastBeams[b.pos] {
  72. pastBeams[b.pos] = true
  73. beams = append(beams, *b)
  74. }
  75. return beams
  76. }
  77. case Slash:
  78. switch b.pos.direction {
  79. case North:
  80. b.pos.direction = East
  81. case South:
  82. b.pos.direction = West
  83. case East:
  84. b.pos.direction = North
  85. case West:
  86. b.pos.direction = South
  87. }
  88. case Backslash:
  89. switch b.pos.direction {
  90. case North:
  91. b.pos.direction = West
  92. case South:
  93. b.pos.direction = East
  94. case East:
  95. b.pos.direction = South
  96. case West:
  97. b.pos.direction = North
  98. }
  99. }
  100. return append(beams, *b)
  101. }
  102. func (b *Beam) move(board [][]byte, height int, width int, pastBeams map[Point]bool) []Beam {
  103. b.wasHere[b.pos] = true
  104. var beams []Beam
  105. directions := b.directions(board, height, width, pastBeams)
  106. for i := range directions {
  107. switch directions[i].pos.direction {
  108. case North:
  109. directions[i].pos.y--
  110. case South:
  111. directions[i].pos.y++
  112. case East:
  113. directions[i].pos.x++
  114. case West:
  115. directions[i].pos.x--
  116. }
  117. 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 {
  118. continue
  119. }
  120. beams = append(beams, directions[i])
  121. }
  122. return beams
  123. }
  124. func emptyBoard(height int, width int) [][]byte {
  125. var board [][]byte
  126. for i := 0; i < height; i++ {
  127. board = append(board, make([]byte, width))
  128. }
  129. return board
  130. }
  131. func count(board [][]byte) int {
  132. var result int
  133. for y := range board {
  134. for x := range board[y] {
  135. if board[y][x] == Mark {
  136. result++
  137. }
  138. }
  139. }
  140. return result
  141. }
  142. func part1(board [][]byte) int {
  143. height := len(board)
  144. width := len(board[0])
  145. trackBoard := emptyBoard(height, width)
  146. beams := []Beam{Beam{pos: Point{y: 0, x: 0, direction: East}, wasHere: make(map[Point]bool)}}
  147. pastBeams := make(map[Point]bool)
  148. pastBeams[beams[0].pos] = true
  149. for {
  150. if len(beams) == 0 {
  151. break
  152. }
  153. var newBeams []Beam
  154. for i := range beams {
  155. if trackBoard[beams[i].pos.y][beams[i].pos.x] != Mark {
  156. trackBoard[beams[i].pos.y][beams[i].pos.x] = Mark
  157. }
  158. newBeams = append(newBeams, beams[i].move(board, height, width, pastBeams)...)
  159. }
  160. beams = newBeams
  161. }
  162. return count(trackBoard)
  163. }
  164. func main() {
  165. if len(os.Args) < 2 {
  166. log.Fatal("You need to specify a file!")
  167. }
  168. filePath := os.Args[1]
  169. file, err := os.Open(filePath)
  170. if err != nil {
  171. log.Fatalf("Failed to open %s!\n", filePath)
  172. }
  173. board := readInput(file)
  174. fmt.Println("Part1:", part1(board))
  175. }