code.go 3.1 KB

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