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)) }