aoc24/day16/dijkstra.go

201 lines
5.9 KiB
Go

package main
import (
"fmt"
"slices"
"git.mstar.dev/mstar/aoc24/util"
"git.mstar.dev/mstar/goutils/sliceutils"
)
type Crossing struct {
At util.Vec2
Exits []util.Vec2
}
type Connection struct {
From, To util.Vec2
Distance int
}
var (
DirUp = util.Vec2{X: 0, Y: -1}
DirRight = util.Vec2{X: 1, Y: 0}
DirDown = util.Vec2{X: 0, Y: 1}
DirLeft = util.Vec2{X: -1, Y: 0}
)
// Get the size of the bord
func getSize(lines []string) util.Vec2 {
return util.Vec2{X: int64(len(lines[0])), Y: int64(len(lines))}
}
// Count the paths next to a given positon
func countPathsAt(lines [][]rune, at util.Vec2) []util.Vec2 {
center := lines[at.Y][at.X]
paths := []util.Vec2{}
if center == '#' {
return paths
}
if util.SafeGet(lines, at.Up()) != '#' {
paths = append(paths, at.Up())
}
if util.SafeGet(lines, at.Down()) != '#' {
paths = append(paths, at.Down())
}
if util.SafeGet(lines, at.Left()) != '#' {
paths = append(paths, at.Left())
}
if util.SafeGet(lines, at.Right()) != '#' {
paths = append(paths, at.Right())
}
return paths
}
func sortPaths(area [][]rune) (all, straights, bends, crossings, deadEnds []util.Vec2) {
for iy, line := range area {
for ix, char := range line {
if char == '#' {
continue
}
pos := util.Vec2{X: int64(ix), Y: int64(iy)}
all = append(all, pos)
entries := countPathsAt(area, pos)
switch len(entries) {
case 0:
continue
case 1:
deadEnds = append(deadEnds, pos)
case 2:
if entries[0].Right().Right().Eq(entries[1]) ||
entries[0].Left().Left().Eq(entries[1]) ||
entries[0].Up().Up().Eq(entries[1]) ||
entries[0].Down().Down().Eq(entries[1]) {
straights = append(straights, pos)
} else {
bends = append(bends, pos)
}
case 3, 4:
crossings = append(crossings, pos)
default:
continue
}
}
}
return
}
// Only works when at a not-straight path node (crossing, bend, dead end)
// TODO: Debug this
func findAdjacentTargets(at util.Vec2, crossings, bends, deadEnds []util.Vec2) []util.Vec2 {
if sliceutils.Contains(crossings, at) || sliceutils.Contains(bends, at) ||
sliceutils.Contains(deadEnds, at) {
return []util.Vec2{}
}
crossingsPosX := sliceutils.Filter(
crossings,
func(t util.Vec2) bool { return at.X < t.X && at.Y == t.Y },
)
slices.SortFunc(crossingsPosX, func(a, b util.Vec2) int { return int(a.X - b.X) })
crossingsNegX := sliceutils.Filter(
crossings,
func(t util.Vec2) bool { return at.X > t.X && at.Y == t.Y },
)
slices.SortFunc(crossingsNegX, func(a, b util.Vec2) int { return int(b.X - a.X) })
crossingsPosY := sliceutils.Filter(
crossings,
func(t util.Vec2) bool { return at.Y < t.Y && at.X == t.X },
)
slices.SortFunc(crossingsPosY, func(a, b util.Vec2) int { return int(a.Y - b.Y) })
crossingsNegY := sliceutils.Filter(
crossings,
func(t util.Vec2) bool { return at.Y > t.Y && at.X == t.X },
)
slices.SortFunc(crossingsNegY, func(a, b util.Vec2) int { return int(b.Y - a.Y) })
bendsPosX := sliceutils.Filter(
bends,
func(t util.Vec2) bool { return at.X < t.X && at.Y == t.Y },
)
slices.SortFunc(bendsPosX, func(a, b util.Vec2) int { return int(a.X - b.X) })
bendsNegX := sliceutils.Filter(
bends,
func(t util.Vec2) bool { return at.X > t.X && at.Y == t.Y },
)
slices.SortFunc(bendsNegX, func(a, b util.Vec2) int { return int(b.X - a.X) })
bendsPosY := sliceutils.Filter(
bends,
func(t util.Vec2) bool { return at.Y < t.Y && at.X == t.X },
)
slices.SortFunc(bendsPosY, func(a, b util.Vec2) int { return int(a.Y - b.Y) })
bendsNegY := sliceutils.Filter(
bends,
func(t util.Vec2) bool { return at.Y > t.Y && at.X == t.X },
)
slices.SortFunc(bendsNegY, func(a, b util.Vec2) int { return int(b.Y - a.Y) })
deadEndsPosX := sliceutils.Filter(
deadEnds,
func(t util.Vec2) bool { return at.X < t.X && at.Y == t.Y },
)
slices.SortFunc(deadEndsPosX, func(a, b util.Vec2) int { return int(a.X - b.X) })
deadEndsNegX := sliceutils.Filter(
deadEnds,
func(t util.Vec2) bool { return at.X > t.X && at.Y == t.Y },
)
slices.SortFunc(deadEndsNegX, func(a, b util.Vec2) int { return int(b.X - a.X) })
deadEndsPosY := sliceutils.Filter(
deadEnds,
func(t util.Vec2) bool { return at.Y < t.Y && at.X == t.X },
)
slices.SortFunc(deadEndsPosY, func(a, b util.Vec2) int { return int(a.Y - b.Y) })
deadEndsNegY := sliceutils.Filter(
deadEnds,
func(t util.Vec2) bool { return at.Y > t.Y && at.X == t.X },
)
slices.SortFunc(deadEndsNegY, func(a, b util.Vec2) int { return int(b.Y - a.Y) })
combined := []util.Vec2{}
combined = append(combined, crossingsPosX...)
combined = append(combined, crossingsNegX...)
combined = append(combined, crossingsPosY...)
combined = append(combined, crossingsNegY...)
combined = append(combined, bendsPosX...)
combined = append(combined, bendsNegX...)
combined = append(combined, bendsPosY...)
combined = append(combined, bendsNegY...)
combined = append(combined, deadEndsPosX...)
combined = append(combined, deadEndsNegX...)
combined = append(combined, deadEndsPosY...)
combined = append(combined, deadEndsNegY...)
return combined
}
func distanceBetween(start, end util.Vec2, crossings, bends, deadEnds []util.Vec2) (int, bool) {
score := -1
if start.Eq(end) {
return 0, true
}
crossings = sliceutils.Filter(crossings, func(v util.Vec2) bool { return !v.Eq(start) })
return score, false
}
func followBend(
at, from util.Vec2,
crossings, bends, deadEnds []util.Vec2,
) (nextCrossingOrDeadEnd util.Vec2, distanceTravelled int) {
if sliceutils.Contains(crossings, at) {
return at, 0
}
return at, 0
}
func main() {
inputLines := util.FileContentToNonEmptyLines(util.LoadFileFromArgs())
// size := getSize(inputLines)
area := sliceutils.Map(inputLines, func(t string) []rune { return []rune(t) })
_, _, bends, crossings, deadEnds := sortPaths(area)
// fmt.Println("straights:", straights)
fmt.Println("bends:", bends)
fmt.Println("crossings:", crossings)
fmt.Println("dead ends:", deadEnds)
fmt.Println("adjacent:", findAdjacentTargets(crossings[0], crossings, bends, deadEnds))
}