mirror of
https://github.com/onyx-and-iris/aoc2024.git
synced 2025-01-10 06:40:47 +00:00
119 lines
2.3 KiB
Go
119 lines
2.3 KiB
Go
package two
|
|
|
|
import (
|
|
"errors"
|
|
"math"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/onyx-and-iris/aoc2024/day-18/internal/queue"
|
|
)
|
|
|
|
type graph struct {
|
|
start point
|
|
end point
|
|
data []string
|
|
}
|
|
|
|
func newGraph(width, height, numCorruptions int, corruptedCoords [][]int) *graph {
|
|
var data []string
|
|
var sb strings.Builder
|
|
for range height {
|
|
for range width {
|
|
sb.WriteRune('.')
|
|
}
|
|
data = append(data, sb.String())
|
|
sb.Reset()
|
|
}
|
|
|
|
for _, coords := range corruptedCoords[:numCorruptions] {
|
|
data[coords[1]] = replaceAtIndex(data[coords[1]], '#', coords[0])
|
|
}
|
|
|
|
return &graph{point{0, 0}, point{len(data[0]) - 1, len(data) - 1}, data}
|
|
}
|
|
|
|
func (g *graph) String() string {
|
|
return strings.Join(g.data, "\n")
|
|
}
|
|
|
|
func (g *graph) isOutOfBounds(p point) bool {
|
|
return p.x < 0 || p.y < 0 || p.y >= len(g.data) || p.x >= len(g.data[p.y])
|
|
}
|
|
|
|
func (g *graph) valueAt(p point) rune {
|
|
return rune(g.data[p.y][p.x])
|
|
}
|
|
|
|
func (g *graph) addCorruption(coords []int) {
|
|
g.data[coords[1]] = replaceAtIndex(g.data[coords[1]], '#', coords[0])
|
|
}
|
|
|
|
func (g *graph) dijkstra(start, end point) ([]point, error) {
|
|
queue := queue.New[point]()
|
|
queue.Enqueue(start)
|
|
visited := make(map[point]struct{})
|
|
costs := make(map[point]int)
|
|
prev := make(map[point]point)
|
|
|
|
for !queue.IsEmpty() {
|
|
current := queue.Dequeue()
|
|
|
|
// we found a shortest path
|
|
if current == end {
|
|
return g.generatePath(start, end, prev), nil
|
|
}
|
|
|
|
_, ok := visited[current]
|
|
if ok {
|
|
continue
|
|
}
|
|
visited[current] = struct{}{}
|
|
|
|
for _, n := range neighbours(current) {
|
|
if g.isOutOfBounds(n) {
|
|
continue
|
|
}
|
|
|
|
if g.valueAt(n) == '#' {
|
|
continue
|
|
}
|
|
|
|
_, ok := costs[n]
|
|
if !ok {
|
|
costs[n] = math.MaxInt
|
|
}
|
|
|
|
new_cost := costs[current] + 1
|
|
if new_cost < costs[n] {
|
|
costs[n] = new_cost
|
|
prev[n] = current
|
|
queue.Enqueue(n)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("unable to find a shortest path")
|
|
}
|
|
|
|
func (g *graph) generatePath(start, end point, prev map[point]point) []point {
|
|
path := []point{end}
|
|
node := prev[end]
|
|
for node != start {
|
|
path = append(path, prev[node])
|
|
node = prev[node]
|
|
}
|
|
return path
|
|
}
|
|
|
|
func (g *graph) debug(path []point) string {
|
|
temp := slices.Clone(g.data)
|
|
for _, p := range path {
|
|
if g.valueAt(p) == '#' {
|
|
continue
|
|
}
|
|
temp[p.y] = replaceAtIndex(temp[p.y], 'O', p.x)
|
|
}
|
|
return strings.Join(temp, "\n")
|
|
}
|