code.go 3.0 KB

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