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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"aoc2024/day13"
|
"aoc2024/day18"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
data, _ := os.ReadFile("day13/input.txt")
|
data, _ := os.ReadFile("day18/input.txt")
|
||||||
fmt.Printf("part 1: %d\n", day13.Part1(string(data)))
|
fmt.Printf("part 1: %d\n", day18.Part1(string(data)))
|
||||||
fmt.Printf("part 2: %d\n", day13.Part2(string(data)))
|
fmt.Printf("part 2: %d\n", day18.Part2(string(data)))
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
fmt.Printf("Execution time: %s\n", elapsed)
|
fmt.Printf("Execution time: %s\n", elapsed)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user