157 lines
3.1 KiB
Go
157 lines
3.1 KiB
Go
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]
|
|
}
|