code.go 3.5 KB

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