This commit is contained in:
Gareth
2024-12-22 19:02:26 +00:00
parent 436e4376a2
commit 622f81a352
4 changed files with 3675 additions and 4 deletions

156
2024/gareth/day18/day18.go Normal file
View 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]
}

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -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)
}