This commit is contained in:
2024-12-18 22:37:36 +01:00
parent 431ee3626a
commit 4572482be6
5 changed files with 3683 additions and 0 deletions

3450
day18/data.txt Normal file

File diff suppressed because it is too large Load Diff

25
day18/example.txt Normal file
View File

@ -0,0 +1,25 @@
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

3
day18/go.mod Normal file
View File

@ -0,0 +1,3 @@
module stevenlr.com/aoc2024/day18
go 1.23.3

204
day18/main.go Normal file
View File

@ -0,0 +1,204 @@
package main
import (
"bufio"
"container/heap"
"fmt"
"math"
"os"
"strconv"
"strings"
)
func main() {
positions := readData("example.txt")
maze := makeMaze(positions[:12], 7)
fmt.Println(part1(maze), 22)
positions2 := readData("data.txt")
maze2 := makeMaze(positions2[:1024], 71)
fmt.Println(part1(maze2), 284)
fmt.Println(6, 1)
fmt.Println(part2(positions, 7, 11))
fmt.Println(part2(positions2, 71, 1023))
}
type Position struct {
x, y int
}
type Candidate struct {
pos Position
estimateCost int
}
type CandidateItem struct {
cnd *Candidate
index int
}
type CandidateQueue struct {
elements []*CandidateItem
index map[Position]*CandidateItem
}
func MakeCandidateQueue() *CandidateQueue {
queue := new(CandidateQueue)
queue.index = make(map[Position]*CandidateItem)
return queue
}
func (queue *CandidateQueue) Len() int {
return len(queue.elements)
}
func (queue *CandidateQueue) Less(i, j int) bool {
return queue.elements[i].cnd.estimateCost < queue.elements[j].cnd.estimateCost
}
func (queue *CandidateQueue) Swap(i, j int) {
queue.elements[i], queue.elements[j] = queue.elements[j], queue.elements[i]
queue.elements[i].index = i
queue.elements[j].index = j
}
func (queue *CandidateQueue) Push(x any) {
candidate := x.(Candidate)
item := &CandidateItem{&candidate, len(queue.elements)}
queue.elements = append(queue.elements, item)
queue.index[candidate.pos] = item
}
func (queue *CandidateQueue) AddElement(candidate Candidate) {
item, alreadyIn := queue.index[candidate.pos]
if alreadyIn {
item.cnd = &candidate
heap.Fix(queue, item.index)
} else {
heap.Push(queue, candidate)
}
}
func (queue *CandidateQueue) Pop() any {
if len(queue.elements) == 0 {
return nil
} else {
elmt := queue.elements[len(queue.elements)-1]
queue.elements = queue.elements[:len(queue.elements)-1]
delete(queue.index, elmt.cnd.pos)
return elmt.cnd
}
}
func makeMaze(walls []Position, size int) (maze [][]byte) {
maze = make([][]byte, size+2)
for y := range maze {
maze[y] = make([]byte, size+2)
maze[y][0] = '#'
maze[y][size+1] = '#'
}
for x := range size + 2 {
maze[0][x] = '#'
maze[size+1][x] = '#'
}
for _, p := range walls {
maze[p.y+1][p.x+1] = '#'
}
return
}
func IntAbs(x int) int {
if x < 0 {
return -x
} else {
return x
}
}
func part1(maze [][]byte) (steps int) {
startX, startY := 1, 1
endX, endY := len(maze)-2, len(maze)-2
estimateCost := func(x, y int) int {
return IntAbs(x-endX) + IntAbs(y-endY)
}
realCost := make([][]int, len(maze))
for y := range realCost {
realCost[y] = make([]int, len(maze))
for x := range realCost[y] {
realCost[y][x] = math.MaxInt
}
}
realCost[startY][startX] = 0
queue := MakeCandidateQueue()
queue.AddElement(Candidate{Position{1, 1}, estimateCost(1, 1)})
for queue.Len() > 0 {
candidate := heap.Pop(queue).(*Candidate)
tryDirection := func(dx, dy int) int {
xNext := candidate.pos.x + dx
yNext := candidate.pos.y + dy
realCostNext := realCost[candidate.pos.y][candidate.pos.x] + 1
if xNext == endX && yNext == endY {
return realCostNext
} else if maze[yNext][xNext] != '#' && realCostNext < realCost[yNext][xNext] {
realCost[yNext][xNext] = realCostNext
queue.AddElement(Candidate{Position{xNext, yNext}, realCostNext + estimateCost(xNext, yNext)})
}
return -1
}
if cost := tryDirection(1, 0); cost >= 0 {
return cost
}
if cost := tryDirection(-1, 0); cost >= 0 {
return cost
}
if cost := tryDirection(0, 1); cost >= 0 {
return cost
}
if cost := tryDirection(0, -1); cost >= 0 {
return cost
}
}
return -1
}
func part2(walls []Position, size int, okAt int) (blockX, blockY int) {
koAt := len(walls)
for koAt-okAt > 1 {
pivot := (okAt + koAt) / 2
maze := makeMaze(walls[:pivot+1], size)
result := part1(maze)
if result < 0 {
koAt = pivot
} else {
okAt = pivot
}
}
return walls[koAt].x, walls[koAt].y
}
func readData(fileName string) (positions []Position) {
fp, _ := os.Open(fileName)
scanner := bufio.NewScanner(fp)
for scanner.Scan() {
line := strings.Split(strings.TrimSpace(scanner.Text()), ",")
x, _ := strconv.Atoi(line[0])
y, _ := strconv.Atoi(line[1])
positions = append(positions, Position{x, y})
}
return
}

View File

@ -10,6 +10,7 @@ use (
./day15
./day16
./day17
./day18
./day2
./day3
./day4