aoc2024/day-18/internal/two/graph.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")
}