code.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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. start.cheats = 2
  41. }
  42. }
  43. y++
  44. }
  45. return start, matrix
  46. }
  47. var directions [][]int = [][]int{
  48. {0, -1}, {1, 0}, {0, 1}, {-1, 0},
  49. }
  50. func getMoves(current Point, matrix [][]byte, xMax, yMax int, cheat bool, cheats map[string]bool) []Point {
  51. var moves []Point
  52. for _, direction := range directions {
  53. move := Point{x: current.x + direction[0], y: current.y + direction[1], cost: current.cost + 1, cheats: current.cheats, cheatedAt: current.cheatedAt}
  54. if move.x <= 0 || move.y <= 0 || move.x >= xMax || move.y >= yMax {
  55. continue
  56. }
  57. if matrix[move.y][move.x] == '#' {
  58. if cheat && !cheats[move.key()] && move.cheats > 0 {
  59. if move.cheatedAt == nil {
  60. move.cheatedAt = &move
  61. }
  62. move.cheats--
  63. moves = append(moves, move)
  64. }
  65. continue
  66. }
  67. if cheat && move.cheats == 1 {
  68. move.cheats = 0
  69. }
  70. moves = append(moves, move)
  71. }
  72. return moves
  73. }
  74. func hike(start *Point, matrix [][]byte, xMax, yMax int, cheat bool, cheats map[string]bool) int {
  75. cost := 1000000000
  76. visited := make(map[string]int)
  77. visited[start.key()] = start.cost
  78. moves := []Point{*start}
  79. for len(moves) > 0 {
  80. current := moves[0]
  81. moves = moves[1:]
  82. if matrix[current.y][current.x] == 'E' && current.cost < cost {
  83. cost = current.cost
  84. if cheat && current.cheatedAt != nil {
  85. cheats[current.cheatedAt.key()] = true
  86. }
  87. }
  88. newMoves := getMoves(current, matrix, xMax, yMax, cheat, cheats)
  89. for _, newMove := range newMoves {
  90. if visited[newMove.key()] == 0 || visited[newMove.key()] > newMove.cost {
  91. moves = append(moves, newMove)
  92. visited[newMove.key()] = newMove.cost
  93. }
  94. }
  95. }
  96. return cost
  97. }
  98. func part1(start *Point, matrix [][]byte) int {
  99. xMax := len(matrix[0]) - 1
  100. yMax := len(matrix) - 1
  101. cheats := make(map[string]bool)
  102. bestWithoutCheating := hike(start, matrix, xMax, yMax, false, cheats)
  103. for {
  104. score := hike(start, matrix, xMax, yMax, true, cheats)
  105. if score >= 1000000000 {
  106. break
  107. }
  108. saving := bestWithoutCheating - score
  109. if saving == 0 {
  110. break
  111. }
  112. fmt.Println(saving, cheats)
  113. }
  114. return 0
  115. }
  116. func main() {
  117. if len(os.Args) < 2 {
  118. log.Fatal("You need to specify a file!")
  119. }
  120. filePath := os.Args[1]
  121. file, err := os.Open(filePath)
  122. if err != nil {
  123. log.Fatalf("Failed to open %s!\n", filePath)
  124. }
  125. start, matrix := readInput(file)
  126. fmt.Println("Part1:", part1(start, matrix))
  127. }