201 lines
5.9 KiB
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))
|
|
}
|