package day18 import ( "container/heap" "strconv" "strings" ) type Point struct { x, y int } // Item represents a node in the priority queue. type Item struct { point Point distance int index int } func Part1(input string) int { grid := parseInput(input, 1024) _, distance := dijkstra(grid) return distance } func Part2(input string) int { lines := strings.Split(strings.TrimSpace(input), "\n") numLines := len(lines) for i := 1; i <= numLines; i++ { grid := parseInput(input, i) _, distance := dijkstra(grid) if distance == -1 { println(lines[i-1]) return 2 } } return -1 } func parseInput(input string, upToLine int) [][]rune { grid := make([][]rune, 71) for i := range grid { grid[i] = make([]rune, 71) for j := range grid[i] { grid[i][j] = '.' } } lines := strings.Split(strings.TrimSpace(input), "\n") for index, line := range lines { if index == upToLine { break } cords := strings.Split(strings.TrimSpace(line), ",") x, _ := strconv.Atoi(cords[0]) y, _ := strconv.Atoi(cords[1]) grid[x][y] = '#' } return grid } // PriorityQueue implements a priority queue for Dijkstra's algorithm. type PriorityQueue []*Item func (pq PriorityQueue) Len() int { return len(pq) } func (pq PriorityQueue) Less(i, j int) bool { return pq[i].distance < pq[j].distance } func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] pq[i].index = i pq[j].index = j } func (pq *PriorityQueue) Push(x interface{}) { n := len(*pq) item := x.(*Item) item.index = n *pq = append(*pq, item) } func (pq *PriorityQueue) Pop() interface{} { old := *pq n := len(old) item := old[n-1] item.index = -1 *pq = old[0 : n-1] return item } func isValid(grid [][]rune, x, y int) bool { return x >= 0 && x < len(grid) && y >= 0 && y < len(grid[0]) && grid[x][y] == '.' } func dijkstra(grid [][]rune) ([][]rune, int) { directions := []Point{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} rows, cols := len(grid), len(grid[0]) // Validate start and end points if grid[0][0] != '.' || grid[rows-1][cols-1] != '.' { return grid, -1 // No path if start or end is not valid } dist := make([][]int, rows) for i := range dist { dist[i] = make([]int, cols) for j := range dist[i] { dist[i][j] = 1 << 30 } } pq := &PriorityQueue{} heap.Init(pq) heap.Push(pq, &Item{point: Point{0, 0}, distance: 0}) dist[0][0] = 0 path := make([][]Point, rows) for i := range path { path[i] = make([]Point, cols) } for pq.Len() > 0 { item := heap.Pop(pq).(*Item) x, y := item.point.x, item.point.y if x == rows-1 && y == cols-1 { break } for _, dir := range directions { nx, ny := x+dir.x, y+dir.y if isValid(grid, nx, ny) { newDist := dist[x][y] + 1 if newDist < dist[nx][ny] { dist[nx][ny] = newDist heap.Push(pq, &Item{point: Point{nx, ny}, distance: newDist}) path[nx][ny] = Point{x, y} } } } } // Check if no path exists if dist[rows-1][cols-1] == 1<<30 { return grid, -1 // No path found } // Reconstruct the path x, y := rows-1, cols-1 for x != 0 || y != 0 { grid[x][y] = 'O' x, y = path[x][y].x, path[x][y].y } grid[0][0] = 'O' return grid, dist[rows-1][cols-1] }