More work on this stupid task
This commit is contained in:
parent
146306f57d
commit
608508dd89
2 changed files with 314 additions and 89 deletions
|
@ -2,10 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"slices"
|
||||
|
||||
"git.mstar.dev/mstar/aoc24/util"
|
||||
"git.mstar.dev/mstar/goutils/sliceutils"
|
||||
"github.com/RyanCarrier/dijkstra/v2"
|
||||
)
|
||||
|
||||
type Crossing struct {
|
||||
|
@ -85,117 +87,333 @@ func sortPaths(area [][]rune) (all, straights, bends, crossings, deadEnds []util
|
|||
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) })
|
||||
func findAdjacentTargets(
|
||||
area [][]rune,
|
||||
at util.Vec2,
|
||||
crossings, bends, deadEnds []util.Vec2,
|
||||
) []util.Vec2 {
|
||||
crossings = sliceutils.Filter(crossings, func(v util.Vec2) bool { return !v.Eq(at) })
|
||||
bends = sliceutils.Filter(bends, func(v util.Vec2) bool { return !v.Eq(at) })
|
||||
deadEnds = sliceutils.Filter(deadEnds, func(v util.Vec2) bool { return !v.Eq(at) })
|
||||
var posX, posY, negX, negY *util.Vec2
|
||||
if util.SafeGet(area, at.Right()) != '#' {
|
||||
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) })
|
||||
if len(crossingsPosX) > 0 {
|
||||
posX = &crossingsPosX[0]
|
||||
}
|
||||
|
||||
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) })
|
||||
if len(bendsPosX) > 0 {
|
||||
if posX == nil {
|
||||
posX = &bendsPosX[0]
|
||||
} else {
|
||||
if util.AbsI(posX.X-at.X) > util.AbsI(bendsPosX[0].X-at.X) {
|
||||
posX = &bendsPosX[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) })
|
||||
if len(deadEndsPosX) > 0 {
|
||||
if posX == nil {
|
||||
posX = &deadEndsPosX[0]
|
||||
} else {
|
||||
if util.AbsI(posX.X-at.X) > util.AbsI(deadEndsPosX[0].X-at.X) {
|
||||
posX = &deadEndsPosX[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if util.SafeGet(area, at.Left()) != '#' {
|
||||
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) })
|
||||
if len(crossingsNegX) > 0 {
|
||||
negX = &crossingsNegX[0]
|
||||
}
|
||||
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) })
|
||||
if len(bendsNegX) > 0 {
|
||||
if negX == nil {
|
||||
negX = &bendsNegX[0]
|
||||
} else {
|
||||
if util.AbsI(negX.X-at.X) > util.AbsI(bendsNegX[0].X-at.X) {
|
||||
negX = &bendsNegX[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
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) })
|
||||
if len(deadEndsNegX) > 0 {
|
||||
if negX == nil {
|
||||
negX = &deadEndsNegX[0]
|
||||
} else {
|
||||
if util.AbsI(negX.X-at.X) > util.AbsI(deadEndsNegX[0].X-at.X) {
|
||||
negX = &deadEndsNegX[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if util.SafeGet(area, at.Down()) != '#' {
|
||||
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) })
|
||||
if len(crossingsPosY) > 0 {
|
||||
posY = &crossingsPosY[0]
|
||||
}
|
||||
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) })
|
||||
if len(bendsPosY) > 0 {
|
||||
if posY == nil {
|
||||
posY = &bendsPosY[0]
|
||||
} else {
|
||||
if util.AbsI(posY.Y-at.Y) < util.AbsI(bendsPosY[0].Y-at.Y) {
|
||||
posY = &bendsPosY[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
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) })
|
||||
if len(deadEndsPosY) > 0 {
|
||||
if posY == nil {
|
||||
posY = &deadEndsPosY[0]
|
||||
} else {
|
||||
if util.AbsI(posY.Y-at.Y) < util.AbsI(deadEndsPosY[0].Y-at.Y) {
|
||||
posY = &deadEndsPosY[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if util.SafeGet(area, at.Up()) != '#' {
|
||||
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) })
|
||||
if len(crossingsNegY) > 0 {
|
||||
negY = &crossingsNegY[0]
|
||||
}
|
||||
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) })
|
||||
if len(bendsNegY) > 0 {
|
||||
if negY == nil {
|
||||
negY = &bendsNegY[0]
|
||||
} else {
|
||||
if util.AbsI(negY.Y-at.Y) > util.AbsI(bendsNegY[0].Y-at.Y) {
|
||||
negY = &bendsNegY[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
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) })
|
||||
if len(deadEndsNegY) > 0 {
|
||||
if negY == nil {
|
||||
negY = &deadEndsNegY[0]
|
||||
} else {
|
||||
if util.AbsI(negY.Y-at.Y) > util.AbsI(deadEndsNegY[0].Y-at.Y) {
|
||||
negY = &deadEndsNegY[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fmt.Printf("posX %#v, negX %#v, posY %#v, negY %#v\n", posX, negX, posY, negY)
|
||||
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...)
|
||||
if posX != nil {
|
||||
combined = append(combined, *posX)
|
||||
}
|
||||
if negX != nil {
|
||||
combined = append(combined, *negX)
|
||||
}
|
||||
if posY != nil {
|
||||
combined = append(combined, *posY)
|
||||
}
|
||||
if negY != nil {
|
||||
combined = append(combined, *negY)
|
||||
}
|
||||
return combined
|
||||
}
|
||||
|
||||
func distanceBetween(start, end util.Vec2, crossings, bends, deadEnds []util.Vec2) (int, bool) {
|
||||
score := -1
|
||||
// Get the distance between two directly connecting points which must be either a crossing or a dead end
|
||||
func distanceBetween(
|
||||
area [][]rune,
|
||||
start, end util.Vec2,
|
||||
crossings, bends, deadEnds []util.Vec2,
|
||||
) (distance int, ok bool) {
|
||||
// Locations are the same, note that and exit
|
||||
if start.Eq(end) {
|
||||
return 0, true
|
||||
}
|
||||
crossings = sliceutils.Filter(crossings, func(v util.Vec2) bool { return !v.Eq(start) })
|
||||
return score, false
|
||||
// TODO: Refactor slightly to allow for distance to straights and bends too
|
||||
|
||||
// Guard against start or end not being either a crossing or dead end
|
||||
if !(sliceutils.Contains(crossings, start) || sliceutils.Contains(deadEnds, start)) &&
|
||||
!(sliceutils.Contains(crossings, end) || sliceutils.Contains(deadEnds, end)) {
|
||||
return 0, false
|
||||
}
|
||||
// Filter out start from crossing and dead ends to avoid recursion or zero length paths
|
||||
// crossings = sliceutils.Filter(crossings, func(v util.Vec2) bool { return !v.Eq(start) })
|
||||
// deadEnds = sliceutils.Filter(deadEnds, func(v util.Vec2) bool { return !v.Eq(start) })
|
||||
adjacents := findAdjacentTargets(area, start, crossings, bends, deadEnds)
|
||||
for _, adj := range adjacents {
|
||||
distanceToStart := int(math.Round(start.Add(adj.Mult(-1)).Len()))
|
||||
trackEnd, trackLen := followBend(area, adj, start, crossings, bends, deadEnds)
|
||||
if trackEnd.Eq(end) {
|
||||
return distanceToStart + trackLen, true
|
||||
}
|
||||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
func followBend(
|
||||
area [][]rune,
|
||||
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
|
||||
if sliceutils.Contains(deadEnds, at) {
|
||||
return at, 0
|
||||
}
|
||||
adjacents := findAdjacentTargets(area, at, crossings, bends, deadEnds)
|
||||
next := sliceutils.Filter(adjacents, func(v util.Vec2) bool { return !v.Eq(from) })[0]
|
||||
diff := at.Add(next.Mult(-1))
|
||||
diffLen := uint64(math.Round(diff.Len()))
|
||||
end, lenToEnd := followBend(area, next, at, crossings, bends, deadEnds)
|
||||
|
||||
return end, int(diffLen) + 1000 + lenToEnd
|
||||
}
|
||||
|
||||
func findAdjacentCrossingsAndDeadEnds(
|
||||
area [][]rune,
|
||||
at util.Vec2,
|
||||
crossings, bends, deadEnds []util.Vec2,
|
||||
) []util.Vec2 {
|
||||
adjs := findAdjacentTargets(area, at, crossings, bends, deadEnds)
|
||||
// fmt.Println("adjacents", adjs)
|
||||
return sliceutils.Map(adjs, func(t util.Vec2) util.Vec2 {
|
||||
end, _ := followBend(area, t, at, crossings, bends, deadEnds)
|
||||
return end
|
||||
})
|
||||
}
|
||||
|
||||
func buildGraph(
|
||||
area [][]rune,
|
||||
crossings, bends, deadEnds []util.Vec2,
|
||||
start, end util.Vec2,
|
||||
) (graph *dijkstra.Graph, poi []util.Vec2, err error) {
|
||||
g := dijkstra.NewGraph()
|
||||
graph = &g
|
||||
poi = slices.Clone(crossings)
|
||||
poi = append(poi, deadEnds...)
|
||||
poi = append(poi, start, end)
|
||||
// Init all vertecies first
|
||||
for i := range poi {
|
||||
err = graph.AddEmptyVertex(i)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for i, pos := range poi {
|
||||
adjs := findAdjacentCrossingsAndDeadEnds(area, pos, crossings, bends, deadEnds)
|
||||
for _, adj := range adjs {
|
||||
index := slices.Index(poi, adj)
|
||||
if index == -1 {
|
||||
panic("Didn't find point in poi")
|
||||
}
|
||||
distance, _ := distanceBetween(area, pos, adj, crossings, bends, deadEnds)
|
||||
err = graph.AddArc(i, index, uint64(distance))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Manually add end inbound connections
|
||||
for _, locs := range findAdjacentCrossingsAndDeadEnds(area, end, crossings, bends, deadEnds) {
|
||||
_ = locs
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func findStartEnd(area [][]rune) (start util.Vec2, end util.Vec2) {
|
||||
for iy, line := range area {
|
||||
for ix, char := range line {
|
||||
if char == 'S' {
|
||||
start = util.Vec2{X: int64(ix), Y: int64(iy)}
|
||||
} else if char == 'E' {
|
||||
end = util.Vec2{X: int64(ix), Y: int64(iy)}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
inputLines := util.FileContentToNonEmptyLines(util.LoadFileFromArgs())
|
||||
// size := getSize(inputLines)
|
||||
area := sliceutils.Map(inputLines, func(t string) []rune { return []rune(t) })
|
||||
start, end := findStartEnd(area)
|
||||
_, _, 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))
|
||||
// fmt.Println("bends:", bends)
|
||||
// fmt.Println("crossings:", crossings)
|
||||
// fmt.Println("dead ends:", deadEnds)
|
||||
fmt.Println(
|
||||
"start adjacents:",
|
||||
findAdjacentCrossingsAndDeadEnds(area, start, crossings, bends, deadEnds),
|
||||
)
|
||||
fmt.Println(
|
||||
"end adjacents:",
|
||||
findAdjacentCrossingsAndDeadEnds(area, util.Vec2{X: 9, Y: 7}, crossings, bends, deadEnds),
|
||||
)
|
||||
dist, ok := distanceBetween(area, util.Vec2{X: 9, Y: 7}, end, crossings, bends, deadEnds)
|
||||
fmt.Println("dist, ok:", dist, ok)
|
||||
// fmt.Println("dist", distanceBetween(area [][]rune, start util.Vec2, end util.Vec2, crossings []util.Vec2, bends []util.Vec2, deadEnds []util.Vec2))
|
||||
graph, pointsOfInterest, err := buildGraph(area, crossings, bends, deadEnds, start, end)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
shortest, err := graph.Shortest(
|
||||
slices.Index(pointsOfInterest, start),
|
||||
slices.Index(pointsOfInterest, end),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("shortest", shortest)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package util
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type Vec2 struct {
|
||||
X, Y int64
|
||||
|
@ -50,6 +53,10 @@ func (v Vec2) IsInBounds(bounds Vec2) bool {
|
|||
return v.X >= 0 && v.X < bounds.X && v.Y >= 0 && v.Y < bounds.Y
|
||||
}
|
||||
|
||||
func (v Vec2) Len() float64 {
|
||||
return math.Sqrt(float64(v.LenSquared()))
|
||||
}
|
||||
|
||||
func (v Vec2) String() string {
|
||||
return fmt.Sprintf("(%d,%d)", v.X, v.Y)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue