Day18
This commit is contained in:
156
2024/gareth/day18/day18.go
Normal file
156
2024/gareth/day18/day18.go
Normal file
@@ -0,0 +1,156 @@
|
||||
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]
|
||||
}
|
||||
65
2024/gareth/day18/day18_test.go
Normal file
65
2024/gareth/day18/day18_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package day18
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPart1(t *testing.T) {
|
||||
r := Part1(`5,4
|
||||
4,2
|
||||
4,5
|
||||
3,0
|
||||
2,1
|
||||
6,3
|
||||
2,4
|
||||
1,5
|
||||
0,6
|
||||
3,3
|
||||
2,6
|
||||
5,1
|
||||
1,2
|
||||
5,5
|
||||
2,5
|
||||
6,5
|
||||
1,4
|
||||
0,4
|
||||
6,4
|
||||
1,1
|
||||
6,1
|
||||
1,0
|
||||
0,5
|
||||
1,6
|
||||
2,0`)
|
||||
assert.Equal(t, 22, r)
|
||||
}
|
||||
|
||||
func TestPart2(t *testing.T) {
|
||||
r := Part2(`5,4
|
||||
4,2
|
||||
4,5
|
||||
3,0
|
||||
2,1
|
||||
6,3
|
||||
2,4
|
||||
1,5
|
||||
0,6
|
||||
3,3
|
||||
2,6
|
||||
5,1
|
||||
1,2
|
||||
5,5
|
||||
2,5
|
||||
6,5
|
||||
1,4
|
||||
0,4
|
||||
6,4
|
||||
1,1
|
||||
6,1
|
||||
1,0
|
||||
0,5
|
||||
1,6
|
||||
2,0`)
|
||||
assert.Equal(t, 0, r)
|
||||
}
|
||||
3450
2024/gareth/day18/input.txt
Normal file
3450
2024/gareth/day18/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"aoc2024/day13"
|
||||
"aoc2024/day18"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
data, _ := os.ReadFile("day13/input.txt")
|
||||
fmt.Printf("part 1: %d\n", day13.Part1(string(data)))
|
||||
fmt.Printf("part 2: %d\n", day13.Part2(string(data)))
|
||||
data, _ := os.ReadFile("day18/input.txt")
|
||||
fmt.Printf("part 1: %d\n", day18.Part1(string(data)))
|
||||
fmt.Printf("part 2: %d\n", day18.Part2(string(data)))
|
||||
elapsed := time.Since(start)
|
||||
fmt.Printf("Execution time: %s\n", elapsed)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user