day6.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package main
  2. import (
  3. "bufio"
  4. "flag"
  5. "fmt"
  6. "os"
  7. "time"
  8. )
  9. func main() {
  10. args := flag.Bool("example", false, "example or input")
  11. flag.Parse()
  12. filename := "input.txt"
  13. if *args {
  14. filename = "example.txt"
  15. }
  16. file, err := os.Open(filename)
  17. if err != nil {
  18. fmt.Println(err)
  19. return
  20. }
  21. defer file.Close()
  22. var grid []string
  23. scanner := bufio.NewScanner(file)
  24. for scanner.Scan() {
  25. grid = append(grid, scanner.Text())
  26. }
  27. if err := scanner.Err(); err != nil {
  28. fmt.Println(err)
  29. return
  30. }
  31. startX, startY, direction := findStart(grid)
  32. start := time.Now()
  33. _, visited := simulate(grid, startX, startY, direction)
  34. lenv := make(map[[2]int]bool)
  35. for x := range visited {
  36. lenv[[2]int{x[0], x[1]}] = true
  37. }
  38. part1_time := time.Since(start)
  39. start = time.Now()
  40. loopPositions := findLoopObstacles(grid, [3]int{startX, startY, directionIndex(direction)}, visited)
  41. part2_time := time.Since(start)
  42. fmt.Println("Visited Distinct Positions:", len(lenv), "in time:", part1_time)
  43. fmt.Println("Loop Positions:", len(loopPositions), "in time:", part2_time)
  44. }
  45. func findStart(grid []string) (int, int, string) {
  46. for y, row := range grid {
  47. for x, cell := range row {
  48. switch cell {
  49. case '^':
  50. return x, y, "up"
  51. case '>':
  52. return x, y, "right"
  53. case '<':
  54. return x, y, "left"
  55. case 'v':
  56. return x, y, "down"
  57. }
  58. }
  59. }
  60. return -1, -1, ""
  61. }
  62. func simulate(grid []string, startX, startY int, direction string) (bool, map[[3]int]bool) {
  63. visited := make(map[[3]int]bool)
  64. directions := map[string][2]int{
  65. "up": {0, -1},
  66. "right": {1, 0},
  67. "down": {0, 1},
  68. "left": {-1, 0},
  69. }
  70. turnRight := map[string]string{
  71. "up": "right",
  72. "right": "down",
  73. "down": "left",
  74. "left": "up",
  75. }
  76. x, y := startX, startY
  77. visited[[3]int{x, y, directionIndex(direction)}] = true
  78. for {
  79. dx, dy := directions[direction][0], directions[direction][1]
  80. nextX, nextY := x+dx, y+dy
  81. if nextY < 0 || nextY >= len(grid) || nextX < 0 || nextX >= len(grid[nextY]) {
  82. return false, visited
  83. }
  84. if grid[nextY][nextX] == '#' {
  85. direction = turnRight[direction]
  86. } else {
  87. x, y = nextX, nextY
  88. }
  89. state := [3]int{x, y, directionIndex(direction)}
  90. if visited[state] {
  91. return true, visited
  92. }
  93. visited[state] = true
  94. }
  95. }
  96. func findLoopObstacles(grid []string, start [3]int, visited map[[3]int]bool) map[[2]int]bool {
  97. originalGrid := make([]string, len(grid))
  98. copy(originalGrid, grid)
  99. loopPositions := make(map[[2]int]bool)
  100. for point := range visited {
  101. x, y, _ := point[0], point[1], point[2]
  102. if x == start[0] && y == start[1] {
  103. continue
  104. }
  105. gridCopy := make([]string, len(originalGrid))
  106. copy(gridCopy, originalGrid)
  107. gridCopy[y] = replaceCharAt(gridCopy[y], x, '#')
  108. cycle, _ := simulate(gridCopy,
  109. // x-indexPairDirection(direction)[0],
  110. // y-indexPairDirection(direction)[1],
  111. // indexDirection(direction+1),
  112. start[0],
  113. start[1],
  114. indexDirection(start[2]),
  115. )
  116. if cycle {
  117. loopPositions[[2]int{x, y}] = true
  118. }
  119. }
  120. return loopPositions
  121. }
  122. func replaceCharAt(s string, index int, newChar rune) string {
  123. runes := []rune(s)
  124. runes[index] = newChar
  125. return string(runes)
  126. }
  127. func directionIndex(direction string) int {
  128. switch direction {
  129. case "up":
  130. return 0
  131. case "right":
  132. return 1
  133. case "down":
  134. return 2
  135. case "left":
  136. return 3
  137. }
  138. return -1
  139. }
  140. func indexDirection(direction int) string {
  141. switch direction % 4 {
  142. case 0:
  143. return "up"
  144. case 1:
  145. return "right"
  146. case 2:
  147. return "down"
  148. case 3:
  149. return "left"
  150. }
  151. return ""
  152. }
  153. func indexPairDirection(direction int) [2]int {
  154. switch direction {
  155. case 0:
  156. return [2]int{0, -1}
  157. case 1:
  158. return [2]int{1, 0}
  159. case 2:
  160. return [2]int{0, 1}
  161. case 3:
  162. return [2]int{-1, 0}
  163. }
  164. return [2]int{0, 0}
  165. }