code.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "log"
  6. "os"
  7. )
  8. type Point struct {
  9. y, x int
  10. cost int
  11. }
  12. func (p *Point) key() string {
  13. return fmt.Sprintf("%d_%d", p.y, p.x)
  14. }
  15. func findPoint(line string, mark byte) *Point {
  16. for i := range line {
  17. if line[i] == mark {
  18. return &Point{x: i}
  19. }
  20. }
  21. return nil
  22. }
  23. func readInput(file *os.File) (*Point, [][]byte) {
  24. scanner := bufio.NewScanner(file)
  25. var matrix [][]byte
  26. var start *Point
  27. var y int
  28. for scanner.Scan() {
  29. line := scanner.Text()
  30. if line == "" {
  31. break
  32. }
  33. matrix = append(matrix, []byte(line))
  34. if start == nil {
  35. start = findPoint(line, 'S')
  36. if start != nil {
  37. start.y = y
  38. }
  39. }
  40. y++
  41. }
  42. return start, matrix
  43. }
  44. var directions [][]int = [][]int{
  45. {0, -1}, {1, 0}, {0, 1}, {-1, 0},
  46. }
  47. func getMoves(current Point, matrix [][]byte, xMax, yMax int, cheat bool, cheats map[string]bool) ([]Point, bool) {
  48. var moves []Point
  49. for _, direction := range directions {
  50. move := Point{x: current.x + direction[0], y: current.y + direction[1], cost: current.cost + 1}
  51. if move.x <= 0 || move.y <= 0 || move.x >= xMax || move.y >= yMax {
  52. continue
  53. }
  54. if matrix[move.y][move.x] == '#' {
  55. if cheat && !cheats[move.key()] {
  56. cheat = false
  57. cheats[move.key()] = true
  58. moves = append(moves, move)
  59. }
  60. continue
  61. }
  62. moves = append(moves, move)
  63. }
  64. return moves, cheat
  65. }
  66. func hike(start *Point, matrix [][]byte, xMax, yMax int, cheat bool, cheats map[string]bool, bestWithoutCheating int, savings map[int]int) int {
  67. cost := 1000000000
  68. visited := make(map[string]int)
  69. visited[start.key()] = start.cost
  70. moves := []Point{*start}
  71. for len(moves) > 0 {
  72. current := moves[0]
  73. moves = moves[1:]
  74. if matrix[current.y][current.x] == 'E' {
  75. if current.cost <= cost {
  76. cost = current.cost
  77. if current.cost < bestWithoutCheating {
  78. saving := bestWithoutCheating - current.cost
  79. savings[saving]++
  80. }
  81. }
  82. continue
  83. }
  84. newMoves, cheated := getMoves(current, matrix, xMax, yMax, cheat, cheats)
  85. if !cheated {
  86. cheat = false
  87. }
  88. for _, newMove := range newMoves {
  89. if visited[newMove.key()] == 0 || visited[newMove.key()] >= newMove.cost {
  90. moves = append(moves, newMove)
  91. visited[newMove.key()] = newMove.cost
  92. }
  93. }
  94. }
  95. return cost
  96. }
  97. func part1(start *Point, matrix [][]byte, atLeast int) int {
  98. xMax := len(matrix[0]) - 1
  99. yMax := len(matrix) - 1
  100. cheats := make(map[string]bool)
  101. savings := make(map[int]int)
  102. bestWithoutCheating := hike(start, matrix, xMax, yMax, false, cheats, 0, savings)
  103. var prevCheats int
  104. for {
  105. hike(start, matrix, xMax, yMax, true, cheats, bestWithoutCheating, savings)
  106. increase := len(cheats)
  107. if prevCheats == increase {
  108. break
  109. }
  110. prevCheats = increase
  111. }
  112. var count int
  113. for key, value := range savings {
  114. if key >= atLeast {
  115. count += value
  116. }
  117. }
  118. return count
  119. }
  120. func main() {
  121. if len(os.Args) < 2 {
  122. log.Fatal("You need to specify a file!")
  123. }
  124. filePath := os.Args[1]
  125. file, err := os.Open(filePath)
  126. if err != nil {
  127. log.Fatalf("Failed to open %s!\n", filePath)
  128. }
  129. start, matrix := readInput(file)
  130. fmt.Println("Part1:", part1(start, matrix, 100))
  131. }