github.com/frodejac/aoc-2022@v0.0.0-20221213081734-037c741b1c89/internal/aoc/day11/day11.go (about) 1 package day11 2 3 import ( 4 "math" 5 "sort" 6 "strconv" 7 "strings" 8 ) 9 10 type point struct { 11 x int 12 y int 13 } 14 15 type Day11 struct { 16 heightMap [][]int 17 startPos point 18 endPos point 19 } 20 21 func parseInput(input string) ([][]int, point, point) { 22 input = strings.TrimSpace(input) 23 lines := strings.Split(input, "\n") 24 var startPos, endPos point 25 heightMap := make([][]int, len(lines)) 26 for i, line := range lines { 27 heightMap[i] = make([]int, len(line)) 28 for j, char := range line { 29 if char == 'S' { 30 startPos = point{x: i, y: j} 31 heightMap[i][j] = 0 32 } else if char == 'E' { 33 endPos = point{x: i, y: j} 34 heightMap[i][j] = int('z') - 97 35 } else { 36 heightMap[i][j] = int(char) - 97 37 } 38 } 39 } 40 41 return heightMap, startPos, endPos 42 } 43 44 func Solver(input []byte) *Day11 { 45 heightMap, startPos, endPos := parseInput(string(input)) 46 return &Day11{heightMap: heightMap, startPos: startPos, endPos: endPos} 47 } 48 49 func dijkstra(w, h int, startPos point, finished func(point) bool, hasPath func(point, point) bool) int16 { 50 dist := make([][]int16, w) 51 visited := make([][]bool, w) 52 for i := range dist { 53 visited[i] = make([]bool, h) 54 dist[i] = make([]int16, h) 55 for j := range dist[i] { 56 dist[i][j] = math.MaxInt16 57 } 58 } 59 dist[startPos.x][startPos.y] = 0 60 queue := []point{startPos} 61 for len(queue) > 0 { 62 curr := queue[0] 63 queue = queue[1:] 64 65 // Skip visited nodes 66 if visited[curr.x][curr.y] { 67 continue 68 } else { 69 visited[curr.x][curr.y] = true 70 } 71 72 // Check if we're done 73 if finished(curr) { 74 return dist[curr.x][curr.y] 75 } 76 77 for _, neighbor := range []point{ 78 {x: curr.x - 1, y: curr.y}, 79 {x: curr.x + 1, y: curr.y}, 80 {x: curr.x, y: curr.y - 1}, 81 {x: curr.x, y: curr.y + 1}, 82 } { 83 // Out of bounds check 84 if neighbor.x < 0 || neighbor.x >= w || neighbor.y < 0 || neighbor.y >= h { 85 continue 86 } 87 88 // Skip inaccessible neighbors 89 if !hasPath(curr, neighbor) { 90 continue 91 } 92 93 // Update shortest path distance 94 alt := dist[curr.x][curr.y] + 1 95 if alt <= dist[neighbor.x][neighbor.y] { 96 dist[neighbor.x][neighbor.y] = alt 97 } 98 queue = append(queue, neighbor) 99 } 100 101 // Sort queue by distance to get a priority queue 102 sort.Slice(queue[:], func(i, j int) bool { 103 return dist[queue[i].x][queue[i].y] < dist[queue[j].x][queue[j].y] 104 }) 105 } 106 return -1 107 } 108 109 func (d *Day11) SolvePart1() string { 110 finished := func(p point) bool { 111 return p.x == d.endPos.x && p.y == d.endPos.y 112 } 113 hasPath := func(p1, p2 point) bool { 114 return (d.heightMap[p2.x][p2.y] - d.heightMap[p1.x][p1.y]) <= 1 115 } 116 w, h := len(d.heightMap), len(d.heightMap[0]) 117 cost := dijkstra(w, h, d.startPos, finished, hasPath) 118 return strconv.Itoa(int(cost)) 119 } 120 121 func (d *Day11) SolvePart2() string { 122 finished := func(p point) bool { 123 return d.heightMap[p.x][p.y] == 0 124 } 125 hasPath := func(p1, p2 point) bool { 126 return (d.heightMap[p1.x][p1.y] - d.heightMap[p2.x][p2.y]) <= 1 127 } 128 w, h := len(d.heightMap), len(d.heightMap[0]) 129 cost := dijkstra(w, h, d.endPos, finished, hasPath) 130 return strconv.Itoa(int(cost)) 131 }