From 146306f57d2272e0bf06128d9bf93d334cb42db6 Mon Sep 17 00:00:00 2001 From: mstar Date: Thu, 16 Jan 2025 17:18:29 +0100 Subject: [PATCH] Lets pick up work on aoc again. Maybe aim for completing it --- day16/dijkstra.go | 201 +++++++++++++++++++++++++++++++++ day16/dijsktra.go.disabled | 220 ------------------------------------- day16/main.go | 214 ------------------------------------ go.mod | 1 - util/loadFile.go | 8 -- util/math.go | 14 ++- util/vec.go | 6 + 7 files changed, 220 insertions(+), 444 deletions(-) create mode 100644 day16/dijkstra.go delete mode 100644 day16/dijsktra.go.disabled delete mode 100644 day16/main.go diff --git a/day16/dijkstra.go b/day16/dijkstra.go new file mode 100644 index 0000000..8c5fdcb --- /dev/null +++ b/day16/dijkstra.go @@ -0,0 +1,201 @@ +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)) +} diff --git a/day16/dijsktra.go.disabled b/day16/dijsktra.go.disabled deleted file mode 100644 index 14e045d..0000000 --- a/day16/dijsktra.go.disabled +++ /dev/null @@ -1,220 +0,0 @@ -package main - -import ( - "fmt" - "slices" - - "git.mstar.dev/mstar/aoc24/util" - "git.mstar.dev/mstar/goutils/sliceutils" - "github.com/RyanCarrier/dijkstra/v2" -) - -type NodeType int - -// A crossing is a path location with 3 or 4 other paths next to it -type Node struct { - At util.Vec2 - Type NodeType - Dirs []util.Vec2 - Index int -} - -const ( - NodeInvalid NodeType = iota - NodeCrossing - NodeCorner - NodeDeadEnd -) - -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} -) - -var startDir = DirRight - -// 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, DirUp) - } - if util.SafeGet(lines, at.Down()) != '#' { - paths = append(paths, DirDown) - } - if util.SafeGet(lines, at.Left()) != '#' { - paths = append(paths, DirLeft) - } - if util.SafeGet(lines, at.Right()) != '#' { - paths = append(paths, DirRight) - } - return paths -} - -func getCrossingsAndCornerPositions(lines [][]rune) []Node { - out := []Node{} - - for iy, line := range lines { - for ix, char := range line { - if char == '#' { - continue - } - pos := util.Vec2{X: int64(ix), Y: int64(iy)} - dirs := countPathsAt(lines, pos) - // Crossings have 3 or more paths out - if len(dirs) >= 3 { - out = append(out, Node{pos, NodeCrossing, dirs, len(out)}) - } - // Also include dead ends - if len(dirs) == 1 { - out = append(out, Node{pos, NodeDeadEnd, dirs, len(out)}) - } - // Location is a corner if the paths are not opposite each other - if len(dirs) == 2 { - if !dirs[0].Mult(-1).Eq(dirs[1]) { - out = append(out, Node{pos, NodeCorner, dirs, len(out)}) - } - } - } - } - return out -} - -func getNeighbourNodes(nodes []Node, index int) []int { - target := nodes[index] - - potential := sliceutils.Filter(nodes, func(t Node) bool { - return t.Index != index && (t.At.X == target.At.X || t.At.Y == target.At.Y) - }) - - hits := []Node{} - for _, dir := range target.Dirs { - switch dir { - case DirUp: - hits = append(hits, slices.MinFunc(potential, func(a, b Node) int { - if a.At.X != target.At.X || a.At.Y > target.At.Y { - return -1 - } - if b.At.X != target.At.X || b.At.Y > target.At.Y { - return 1 - } - return util.AbsI( - int(target.At.Y)-int(a.At.Y), - ) - util.AbsI( - int(target.At.Y)-int(b.At.Y), - ) - })) - case DirDown: - hits = append(hits, slices.MinFunc(potential, func(a, b Node) int { - if a.At.X != target.At.X || a.At.Y < target.At.Y { - return -1 - } - if b.At.X != target.At.X || b.At.Y < target.At.Y { - return 1 - } - return util.AbsI( - int(target.At.Y)-int(a.At.Y), - ) - util.AbsI( - int(target.At.Y)-int(b.At.Y), - ) - })) - case DirLeft: - hits = append(hits, slices.MinFunc(potential, func(a, b Node) int { - if a.At.Y != target.At.Y || a.At.X > target.At.X { - return -1 - } - if b.At.Y != target.At.Y || b.At.X > target.At.X { - return 1 - } - return util.AbsI( - int(target.At.X)-int(a.At.X), - ) - util.AbsI( - int(target.At.X)-int(b.At.X), - ) - })) - case DirRight: - hits = append(hits, slices.MinFunc(potential, func(a, b Node) int { - if a.At.Y != target.At.Y || a.At.X > target.At.X { - return -1 - } - if b.At.Y != target.At.Y || b.At.X > target.At.X { - return 1 - } - return util.AbsI( - int(target.At.X)-int(a.At.X), - ) - util.AbsI( - int(target.At.X)-int(b.At.X), - ) - })) - default: - panic("Unknown dir") - } - } - return sliceutils.Map(hits, func(t Node) int { return t.Index }) -} - -func addNodesToGraph(nodes []Node, graph *dijkstra.Graph) { - for i := range len(nodes) { - if err := graph.AddEmptyVertex(i); err != nil { - panic(err) - } - } - for _, node := range nodes { - for _, n := range getNeighbourNodes(nodes, node.Index) { - if err := graph.AddArc(node.Index, n, node.At.Add(nodes[n].At.Mult(-1)).LenSquared()); err != nil { - panic(err) - } - } - } -} - -func findEnd(lines [][]rune) util.Vec2 { - for iy, line := range lines { - for ix, char := range line { - if char == 'E' { - return util.Vec2{X: int64(ix), Y: int64(iy)} - } - } - } - return util.Vec2{X: -1, Y: -1} -} - -func findStart(lines [][]rune) util.Vec2 { - for iy, line := range lines { - for ix, char := range line { - if char == 'S' { - return util.Vec2{X: int64(ix), Y: int64(iy)} - } - } - } - return util.Vec2{X: -1, Y: -1} -} - -func main() { - inputLines := util.FileContentToNonEmptyLines(util.LoadFileFromArgs()) - // size := getSize(inputLines) - inputLineChars := sliceutils.Map(inputLines, func(t string) []rune { return []rune(t) }) - nodes := getCrossingsAndCornerPositions(inputLineChars) - // fmt.Println(nodes) - // slices.MaxFunc(x S, cmp func(a E, b E) int) - graph := dijkstra.NewGraph() - addNodesToGraph(nodes, &graph) - startVec := findStart(inputLineChars) - endVec := findEnd(inputLineChars) - startI := slices.IndexFunc(nodes, func(e Node) bool { return e.At == startVec }) - endI := slices.IndexFunc(nodes, func(e Node) bool { return e.At == endVec }) - - path, err := graph.Shortest(startI, endI) - fmt.Println(path, err) -} diff --git a/day16/main.go b/day16/main.go deleted file mode 100644 index aff9c88..0000000 --- a/day16/main.go +++ /dev/null @@ -1,214 +0,0 @@ -package main - -// Look at https://topaz.github.io/paste/#XQAAAQB1CAAAAAAAAAA4GEiZzRd1JAgz+whYRQxSFI7XvmlfhtGDinecZE7XVglOhhPLih7tF+1f6vGBXHx2f7N/fIfjMG8zy2b5K1bcEP2Fo6yufIOrK2Xv9CFqxAiFcqXF+B4dWDdaic6Mhmi96bCxTu3B1MuJ8XPiKzAsC1j5U5qhN1K9v/wrCnRnSs483gy4bYiKZacHfPN6AsZrOcqsgagmb5XZWYgYt6yR6lJ7yx0goiNLILnmpT8E9yBh5igU8VCxKT8lpj05Ce5ffUlcz4mCgQC5jenYOl86fCFPgFC05AvKYVOmGpjW8syIdhCLxhQJnhNsljM5wHIUezS9qwRsjXZTfItq30EnkIM9mdSPZY2/eGBlasPQi5LR+l5JFw9w3W3fesXYEB2u9yc/umujdgTI4RikSMdOfdYOrgCLuPFA72fHU3tGk2oyL5k58ZAR1SKFm+7XExv3gFPh6t+mrFjJ9QZ7qEb19gn0kMt2MaSiNLzyB8cJ9Dr3yxTGqfBOPsJPSI2jM2H1xgg1fu7gBZAYu5+KQ1CyctZ9mFzm25QxvrNyLFoNTPKn9ikZw7kUmqL6mIwu4bdJPwHivcPJTg0gNw03vByP90Yd/JGfGc+sFDd3V3R+LWLj3r3vIqs4QiSSgF/FOsQQjPPCt51Px1gtSR0opaqNeKHOAZirql6UaFuE/UPgUVH87T/q/Qi+jeLtc+pE3KySx42V4YJvPFEZKYCmGhrt1inGQXpS4K53V+VuCMBmo0jFayu8azXUyXaf2TgAtRNMmzCr2UmW+XnhCuMrdiEzj/vEH6WMrnJsfQlB8LrkwLYjdp16kMGavefwDk84W4C0lIcaDLRrxd+z5tN8+6jBpamqrsJy7BiKbDq7swhTXGoss0Xl2ZychI1h95+ZkFRQPm2ykeIwRcMwUIIA3WE8mXvo79noiPpQgasyUEqfGBEulP9/AaIidnEmK20OPFW4iwxCy/DinEok9tpMRocHYYQyyjFiqgd4D4blrPIDPjy0PE+zGhhrpyOOPEL7lEOoLCOHm8E54KuYX0eX1jnnjOSBlhO2//OOAmyWb2adhy5ir5ZWsqnXQaLo0FPwefYre1c6QuppDgiDgww0DhyroJ4YLvEn70ELBqlTIlcHI2XLDrD2wJF8lKa0TzrS3W+o3w86g1WSRLOWmcaCinUYEFSgaGsJzkNjkHS0IILRn2pWSxHqDxE5o2vR6ji41ijmNUDfq2gM9Gi+gTs+WgPKG+a4RX9qWY3aNqRleT89ZdORfsfz5/H1B0R7NDXcvoUY8fZ76k0ylqS1uc4Dc2ZTynjcufAHl4PbX9Sd/X1rXgvWac3RO5F8ztK8ESr1LqYZDqrQYNiwY+7F6P/W/BYU -// and https://topaz.github.io/paste/#XQAAAQDkCQAAAAAAAAA4GEiZzRd1JAgz+whYRQxSFI7XvmlfhtGDinecZE7XVglOhhPLih7tF+1f6vGBXHx2f7N/fIfjMG8zy2b5K1bcEP2Fo6yufIOrK2Xv9CFqxAiFcqXF+B4dWDZXMbthHyfiUnWQNzkV0hcT5wVw9evKcKh6zuVp40d6Delxu4q84FVJ2dhY/eNJLsgsqMaSnCN8+e4FFMobzvc4ZXLFZQrIjVJ0UJwQ7ZKOvJDlemAVoOBoABHsgOgAJCFm4JV2lELwBITIKT9LzmRsO7giPuslQ7Zsz3Hs9czf3v/ly4cWYb2QQMsQPahsGW2aIYGEflk2nhYJPAosdN9zZ285PGXNNrW5e9NHP5AnsS9lkvf3Kedlg2bQd+ByV/EhMGpwOQjNYG0JpZBF5rJzWVdsuYwvetCboF9ZqInecZftwTsJtyTV2P70EYjs5ipojuXXieZ6QG/XxI/aDOh4WXuWORVA0TxxMtLzuiUElWvTdAjA17uwb1/1i12tbP6s6EFNB6IMgaZnQv0o2zAcIH7em3/HdDXdRytvqC96G34ml/kFYUvvQu1tbK0f9lZIfUtPt30fKSM2zg4NJ60S2DNHt8zU6JvrGqyk/2CPSoeineqtMlQLoTm+a+jRr0FcUEpSMNWYm5bOpcllImxoVUXA7HV0FTDYM0yo8q/pMptPAp/971Z4HCMjsOyJTKuQeAXK8iIYqWaXXpcyhwaaDZ99+p8rlz5K1xAT308o5A4ZdcIALJ3ZEtNlD/9Y5aZ7+Ynngr4O72arMxGuS6wAUj001ocWXbSOEGlmGeCWVUdIEuRF2RQUlcBzFluDR9FeUkVoGqnjGLQ1MWSyRCfKm2btiWELjge8ge4TZEd7KVG5Nrm8cKrZDAYjZ0xkO8p8nnSTlIGJ1Vft1y7SEsxexvx0O0Flz4rQg1Z6SR1JNqOuTkGjhirnSdBN+r6Mvs2Fqb/Kq/FJlHe+kXSF1bitp2skfxdiFYqEEdL+unScYL7SAczwANBr/IiznSJyyL9zfBYvXQcVerkWkfAlm5ape09UaxgGv9LKREALhYAzN6AJDDB9c6tRbN+plzVIRN9CBmDVLReSIo7R8E0etA8L3yhbe2BBLfN4lL2gAXtzzJjYvkUp9raqCz61WMvHreUD2PhTazl+kwjM8IUzmo2DFrXU1ZzOF/T7XWXrjlqgOzBmEE/rldgY0ddx8e0vSj7gKD8EUIPAlR6trZcdCA0L2ENYbOisKQnTn/cfnHJl8NeXH0AOxNKw+V+mRh3W7S747+nBiRmseGkDM17g3MR6hoVdW3V3thtUuwgtP7VBfwdvjlumN1m2L3a6of+hqL7djDL3IzQQBnlhEdszcToONrRBmVRJOMf0ECPY5A9brSO1jR6sVTjQF1nKabW6tEFLuVHcr+BiKUP+pZKKz8amUuffmQixFGfeWUKS0RJTMJt8TVzczSpqNvaJ02r+5qgkN5PddKwMufcpMeTEQy4IRFTCwM6spWm9FVB/8uR3Htfv1KgA0Z6cSkH/+Y3ogw== -// Maybe also https://github.com/AhmedYassineMaalej/AoC-2024/blob/master/src/problems/day16.rs -// And more https://github.com/ayoubzulfiqar/advent-of-code/blob/main/2024/Go/Day16/part_1.go - -import ( - "fmt" - "math" - "slices" - "strings" - - "git.mstar.dev/mstar/goutils/sliceutils" - - "git.mstar.dev/mstar/aoc24/util" -) - -// A crossing is a path location with 3 or 4 other paths next to it -type Crossing struct { - At util.Vec2 - LowestScore int - LowestFrom util.Vec2 // Index into list of crossings - Neighbours []util.Vec2 // Directions where a path is -} - -type Area map[int64]map[int64]*Crossing - -type Wrapper struct { - A Area -} - -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} -) - -var startDir = DirRight - -// 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 linesToMap(lines [][]rune, size util.Vec2) Area { - area := Area{} - // Ensure area has full maps - for i := range size.Y { - area[i] = map[int64]*Crossing{} - } - for iy, line := range lines { - for ix, char := range line { - pos := util.Vec2{X: int64(ix), Y: int64(iy)} - switch char { - case '.', 'E', 'S': - dirs := countPathsAt(lines, pos) - area[int64(ix)][int64(iy)] = &Crossing{ - At: pos, - LowestScore: math.MaxInt64, - LowestFrom: util.Vec2{X: -1, Y: -1}, - Neighbours: dirs, - } - default: - } - } - } - return area -} - -func findStart(lines [][]rune) util.Vec2 { - for iy, line := range lines { - for ix, char := range line { - if char == 'S' { - return util.Vec2{X: int64(ix), Y: int64(iy)} - } - } - } - // Return invalid positon if none found - return util.Vec2{X: -1, Y: -1} -} - -func findEnd(lines [][]rune) util.Vec2 { - for iy, line := range lines { - for ix, char := range line { - if char == 'E' { - return util.Vec2{X: int64(ix), Y: int64(iy)} - } - } - } - // Return invalid positon if none found - return util.Vec2{X: -1, Y: -1} -} - -func walk( - area *Wrapper, - from util.Vec2, - dir util.Vec2, - score int, -) { - // Idea: walk forward from start until crossing or wall - // At crossing, defer a walker for both sides (if not walled off) but prioritise forwards - // At crossing, check if own score is lower than current score - // If it is, update score and set own origin - // Else if crossing has a lower score than self, abort - - // Special case: - // If at end, update end "crossing" following the earlier rules - - // Walk forward in dir until no node found - type WalkTarget struct { - Pos, Dir util.Vec2 - Score int - } - // targets := []WalkTarget{} - defer fmt.Println("Deffered done", from) - fmt.Println("Starting", from) - prev := from - for pos := from.Add(dir); area.A[pos.X][pos.Y] != nil; pos = pos.Add(dir) { - score++ - node := area.A[pos.X][pos.Y] - if node.LowestScore <= score { - return - } - node.LowestScore = score - node.LowestFrom = prev - // node.LowestFrom = pos.Add(dir.Mult(-1)) - fmt.Println("Setting node", pos, score, node.LowestFrom) - // Get all directions that don't match current walking direction (and reverse) - filtered := sliceutils.Filter(node.Neighbours, func(t util.Vec2) bool { - return !t.Add(pos.Mult(-1)).Eq(dir) && !t.Add(pos.Mult(-1)).Eq(dir.Mult(-1)) - }) - // fmt.Println("Filtered neighbours of node", filtered) - for _, neighbour := range filtered { - fmt.Println("Adding target", neighbour) - // targets = append(targets, WalkTarget{ - // neighbour, neighbour.Add(pos.Mult(-1)), score + 1001, - // }) - defer walk(area, neighbour, neighbour.Add(pos.Mult(-1)), score+1001) - } - fmt.Println("Stepping", dir) - prev = pos - } - // fmt.Println("Hitting stored targets", from) - // for _, target := range targets { - // walk(area, target.Pos, target.Dir, target.Score) - // } - fmt.Println("Done, walking deferred", from) -} - -func visualisePath(area Area, endPos, size util.Vec2, inputLineRunes [][]rune) string { - lineCopy := [][]rune{} - for _, line := range inputLineRunes { - lineCopy = append(lineCopy, slices.Clone(line)) - } - for pos := endPos; pos.IsInBounds(size); pos = area[pos.X][pos.Y].LowestFrom { - fmt.Println("Path elem", pos) - lineCopy[pos.Y][pos.X] = 'x' - } - builder := strings.Builder{} - for _, line := range lineCopy { - for _, char := range line { - builder.WriteRune(char) - } - builder.WriteRune('\n') - } - return builder.String() -} - -func main() { - inputLines := util.FileContentToNonEmptyLines(util.LoadFileFromArgs()) - size := getSize(inputLines) - inputLineChars := sliceutils.Map(inputLines, func(t string) []rune { return []rune(t) }) - area := linesToMap(inputLineChars, size) - - start := findStart(inputLineChars) - end := findEnd(inputLineChars) - // Fill entire map - fmt.Println("Filling right") - wrapped := Wrapper{area} - walk(&wrapped, start, DirRight, 0) - fmt.Println("Filling up") - walk(&wrapped, start, DirUp, 1000) - fmt.Println("Filling down") - walk(&wrapped, start, DirDown, 1000) - fmt.Println("Filling left") - walk(&wrapped, start, DirLeft, 2000) - fmt.Printf("%#v\n", area[start.X][start.Y]) - fmt.Printf("Task 1: %d\n", area[end.X][end.Y].LowestScore) - fmt.Println(visualisePath(area, end, size, inputLineChars)) -} diff --git a/go.mod b/go.mod index a19d5b8..8a03795 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,5 @@ go 1.23.3 require git.mstar.dev/mstar/goutils v1.5.4 require ( - github.com/RyanCarrier/dijkstra v1.4.0 // indirect github.com/RyanCarrier/dijkstra/v2 v2.0.2 // indirect ) diff --git a/util/loadFile.go b/util/loadFile.go index 70ced59..642f400 100644 --- a/util/loadFile.go +++ b/util/loadFile.go @@ -19,14 +19,6 @@ func LoadFileFromArgs() []byte { return data } -func AbsI(a int) int { - if a > 0 { - return a - } else { - return -a - } -} - func FileContentToNonEmptyLines(data []byte) []string { return sliceutils.Filter( strings.Split(string(data), "\n"), diff --git a/util/math.go b/util/math.go index 0313c11..0b54d85 100644 --- a/util/math.go +++ b/util/math.go @@ -1,6 +1,10 @@ package util -func MaxI(x ...int) int { +type Unsigned interface { + int | int8 | int16 | int32 | int64 | float32 | float64 +} + +func MaxI[T Unsigned](x ...T) T { if len(x) == 0 { return 0 } @@ -12,3 +16,11 @@ func MaxI(x ...int) int { } return tmp } + +func AbsI[T Unsigned](a T) T { + if a > 0 { + return a + } else { + return -a + } +} diff --git a/util/vec.go b/util/vec.go index aff9ffc..21a477f 100644 --- a/util/vec.go +++ b/util/vec.go @@ -1,5 +1,7 @@ package util +import "fmt" + type Vec2 struct { X, Y int64 } @@ -47,3 +49,7 @@ func (v Vec2) LenSquared() uint64 { 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) String() string { + return fmt.Sprintf("(%d,%d)", v.X, v.Y) +}