From 22b442171b823260f2f5564a8e528d266f209c50 Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Thu, 19 Dec 2024 21:32:21 +0000 Subject: [PATCH] add point subpackage build ShortestPath as ordered map replace dijkstra in part two with a bfs --- day-18/.gitignore | 1 + day-18/benchmark | 22 ++++---- day-18/go.mod | 13 ++++- day-18/go.sum | 16 +++++- day-18/internal/one/benchmark | 4 +- day-18/internal/one/graph.go | 20 +++++--- day-18/internal/one/neighbours.go | 14 ++--- day-18/internal/one/solve.go | 20 +++++--- day-18/internal/{one => point}/point.go | 2 +- day-18/internal/two/benchmark | 4 +- day-18/internal/two/graph.go | 68 +++++++++---------------- day-18/internal/two/neighbours.go | 14 ++--- day-18/internal/two/point.go | 6 --- day-18/internal/two/solve.go | 12 ++--- 14 files changed, 111 insertions(+), 105 deletions(-) create mode 100644 day-18/.gitignore rename day-18/internal/{one => point}/point.go (72%) delete mode 100644 day-18/internal/two/point.go diff --git a/day-18/.gitignore b/day-18/.gitignore new file mode 100644 index 0000000..e0e6347 --- /dev/null +++ b/day-18/.gitignore @@ -0,0 +1 @@ +run.sh \ No newline at end of file diff --git a/day-18/benchmark b/day-18/benchmark index 96304c5..4140a83 100644 --- a/day-18/benchmark +++ b/day-18/benchmark @@ -2,14 +2,14 @@ goos: linux goarch: amd64 pkg: github.com/onyx-and-iris/aoc2024/day-18 cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz -BenchmarkSolve-12 1 3840363786 ns/op -BenchmarkSolve-12 1 3847307489 ns/op -BenchmarkSolve-12 1 3793560090 ns/op -BenchmarkSolve-12 1 3804761992 ns/op -BenchmarkSolve-12 1 3811796394 ns/op -BenchmarkSolve-12 1 3782742297 ns/op -BenchmarkSolve-12 1 3799861901 ns/op -BenchmarkSolve-12 1 3784385312 ns/op -BenchmarkSolve-12 1 3780708522 ns/op -BenchmarkSolve-12 1 3811926561 ns/op -ok github.com/onyx-and-iris/aoc2024/day-18 38.067s +BenchmarkSolve-12 1 1410266797 ns/op +BenchmarkSolve-12 1 1422531597 ns/op +BenchmarkSolve-12 1 1422451797 ns/op +BenchmarkSolve-12 1 1404297097 ns/op +BenchmarkSolve-12 1 1391816300 ns/op +BenchmarkSolve-12 1 1416344400 ns/op +BenchmarkSolve-12 1 1399980801 ns/op +BenchmarkSolve-12 1 1391180300 ns/op +BenchmarkSolve-12 1 1390386701 ns/op +BenchmarkSolve-12 1 1399453200 ns/op +ok github.com/onyx-and-iris/aoc2024/day-18 14.058s diff --git a/day-18/go.mod b/day-18/go.mod index 210963f..2b1ed4a 100644 --- a/day-18/go.mod +++ b/day-18/go.mod @@ -2,6 +2,15 @@ module github.com/onyx-and-iris/aoc2024/day-18 go 1.23.3 -require github.com/sirupsen/logrus v1.9.3 +require ( + github.com/sirupsen/logrus v1.9.3 + github.com/wk8/go-ordered-map/v2 v2.1.8 +) -require golang.org/x/sys v0.12.0 // indirect +require ( + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + golang.org/x/sys v0.12.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/day-18/go.sum b/day-18/go.sum index 8d5c188..2d783b3 100644 --- a/day-18/go.sum +++ b/day-18/go.sum @@ -1,16 +1,28 @@ +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/day-18/internal/one/benchmark b/day-18/internal/one/benchmark index 3316dc1..809bcda 100644 --- a/day-18/internal/one/benchmark +++ b/day-18/internal/one/benchmark @@ -2,5 +2,5 @@ goos: linux goarch: amd64 pkg: github.com/onyx-and-iris/aoc2024/day-18/internal/one cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz -BenchmarkSolve-12 1000000000 0.004050 ns/op -ok github.com/onyx-and-iris/aoc2024/day-18/internal/one 0.029s +BenchmarkSolve-12 1000000000 0.003678 ns/op +ok github.com/onyx-and-iris/aoc2024/day-18/internal/one 0.030s diff --git a/day-18/internal/one/graph.go b/day-18/internal/one/graph.go index 1862864..fb91968 100644 --- a/day-18/internal/one/graph.go +++ b/day-18/internal/one/graph.go @@ -3,11 +3,14 @@ package one import ( "slices" "strings" + + "github.com/onyx-and-iris/aoc2024/day-18/internal/point" + orderedmap "github.com/wk8/go-ordered-map/v2" ) type graph struct { - start Point - end Point + start point.Point + end point.Point data []string } @@ -26,25 +29,26 @@ func newGraph(width, height, numCorruptions int, corruptedCoords [][]int) *graph data[coords[1]] = replaceAtIndex(data[coords[1]], '#', coords[0]) } - return &graph{Point{0, 0}, Point{len(data[0]) - 1, len(data) - 1}, data} + return &graph{point.Point{X: 0, Y: 0}, point.Point{X: len(data[0]) - 1, Y: len(data) - 1}, data} } func (g *graph) String() string { return strings.Join(g.data, "\n") } -func (g *graph) isOutOfBounds(p Point) bool { +func (g *graph) isOutOfBounds(p point.Point) bool { return p.X < 0 || p.Y < 0 || p.Y >= len(g.data) || p.X >= len(g.data[p.Y]) } -func (g *graph) valueAt(p Point) rune { +func (g *graph) valueAt(p point.Point) rune { return rune(g.data[p.Y][p.X]) } -func (g *graph) debug(path []Point) string { +func (g *graph) debug(path *orderedmap.OrderedMap[point.Point, struct{}]) string { temp := slices.Clone(g.data) - for _, p := range path { - temp[p.Y] = replaceAtIndex(temp[p.Y], 'O', p.X) + temp[g.start.Y] = replaceAtIndex(temp[g.start.Y], 'O', g.start.X) + for pair := path.Oldest(); pair != nil; pair = pair.Next() { + temp[pair.Key.Y] = replaceAtIndex(temp[pair.Key.Y], 'O', pair.Key.X) } return strings.Join(temp, "\n") } diff --git a/day-18/internal/one/neighbours.go b/day-18/internal/one/neighbours.go index d941e8e..357a0a8 100644 --- a/day-18/internal/one/neighbours.go +++ b/day-18/internal/one/neighbours.go @@ -1,10 +1,12 @@ package one -func neighbours(p Point) [4]Point { - return [4]Point{ - {p.X, p.Y - 1}, // N - {p.X + 1, p.Y}, // E - {p.X, p.Y + 1}, // S - {p.X - 1, p.Y}, // W +import "github.com/onyx-and-iris/aoc2024/day-18/internal/point" + +func neighbours(p point.Point) [4]point.Point { + return [4]point.Point{ + {X: p.X, Y: p.Y - 1}, // N + {X: p.X + 1, Y: p.Y}, // E + {X: p.X, Y: p.Y + 1}, // S + {X: p.X - 1, Y: p.Y}, // W } } diff --git a/day-18/internal/one/solve.go b/day-18/internal/one/solve.go index 069f0a9..f9eb5f7 100644 --- a/day-18/internal/one/solve.go +++ b/day-18/internal/one/solve.go @@ -5,11 +5,14 @@ import ( "math" "github.com/onyx-and-iris/aoc2024/day-18/internal/config" + "github.com/onyx-and-iris/aoc2024/day-18/internal/point" "github.com/onyx-and-iris/aoc2024/day-18/internal/queue" log "github.com/sirupsen/logrus" + + orderedmap "github.com/wk8/go-ordered-map/v2" ) -var ShortestPath []Point +var ShortestPath *orderedmap.OrderedMap[point.Point, struct{}] func Solve(buf []byte, config config.Config) (int, error) { r := bytes.NewReader(buf) @@ -21,11 +24,11 @@ func Solve(buf []byte, config config.Config) (int, error) { log.Debugf("start: %v end: %v", graph.start, graph.end) log.Debugf("\n%s\n", graph.String()) - queue := queue.New[Point]() + queue := queue.New[point.Point]() queue.Enqueue(graph.start) - visited := make(map[Point]struct{}) - costs := make(map[Point]int) - prev := make(map[Point]Point) + visited := make(map[point.Point]struct{}) + costs := make(map[point.Point]int) + prev := make(map[point.Point]point.Point) for !queue.IsEmpty() { current := queue.Dequeue() @@ -63,14 +66,15 @@ func Solve(buf []byte, config config.Config) (int, error) { } } - ShortestPath = []Point{graph.end} + ShortestPath = orderedmap.New[point.Point, struct{}]() + ShortestPath.Set(graph.end, struct{}{}) node := prev[graph.end] for node != graph.start { - ShortestPath = append(ShortestPath, prev[node]) + ShortestPath.Set(node, struct{}{}) node = prev[node] } log.Debugf("\n%s\n", graph.debug(ShortestPath)) - return len(ShortestPath), nil + return ShortestPath.Len(), nil } diff --git a/day-18/internal/one/point.go b/day-18/internal/point/point.go similarity index 72% rename from day-18/internal/one/point.go rename to day-18/internal/point/point.go index e54fb23..e5fcaa0 100644 --- a/day-18/internal/one/point.go +++ b/day-18/internal/point/point.go @@ -1,4 +1,4 @@ -package one +package point type Point struct { X int diff --git a/day-18/internal/two/benchmark b/day-18/internal/two/benchmark index 3021b46..adda491 100644 --- a/day-18/internal/two/benchmark +++ b/day-18/internal/two/benchmark @@ -2,5 +2,5 @@ goos: linux goarch: amd64 pkg: github.com/onyx-and-iris/aoc2024/day-18/internal/two cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz -BenchmarkSolve-12 1 3756699083 ns/op -ok github.com/onyx-and-iris/aoc2024/day-18/internal/two 3.760s +BenchmarkSolve-12 1 1414311557 ns/op +ok github.com/onyx-and-iris/aoc2024/day-18/internal/two 1.418s diff --git a/day-18/internal/two/graph.go b/day-18/internal/two/graph.go index 83fe183..7487688 100644 --- a/day-18/internal/two/graph.go +++ b/day-18/internal/two/graph.go @@ -1,17 +1,17 @@ package two import ( - "errors" - "math" "slices" "strings" + "github.com/onyx-and-iris/aoc2024/day-18/internal/point" "github.com/onyx-and-iris/aoc2024/day-18/internal/queue" + log "github.com/sirupsen/logrus" ) type graph struct { - start point - end point + start point.Point + end point.Point data []string } @@ -30,38 +30,35 @@ func newGraph(width, height, numCorruptions int, corruptedCoords [][]int) *graph data[coords[1]] = replaceAtIndex(data[coords[1]], '#', coords[0]) } - return &graph{point{0, 0}, point{len(data[0]) - 1, len(data) - 1}, data} + return &graph{point.Point{X: 0, Y: 0}, point.Point{X: len(data[0]) - 1, Y: len(data) - 1}, data} } func (g *graph) String() string { return strings.Join(g.data, "\n") } -func (g *graph) isOutOfBounds(p point) bool { - return p.x < 0 || p.y < 0 || p.y >= len(g.data) || p.x >= len(g.data[p.y]) +func (g *graph) isOutOfBounds(p point.Point) bool { + return p.X < 0 || p.Y < 0 || p.Y >= len(g.data) || p.X >= len(g.data[p.Y]) } -func (g *graph) valueAt(p point) rune { - return rune(g.data[p.y][p.x]) +func (g *graph) valueAt(p point.Point) rune { + return rune(g.data[p.Y][p.X]) } -func (g *graph) addCorruption(coords []int) { - g.data[coords[1]] = replaceAtIndex(g.data[coords[1]], '#', coords[0]) +func (g *graph) addCorruption(p point.Point) { + g.data[p.Y] = replaceAtIndex(g.data[p.Y], '#', p.X) } -func (g *graph) dijkstra(start, end point) ([]point, error) { - queue := queue.New[point]() - queue.Enqueue(start) - visited := make(map[point]struct{}) - costs := make(map[point]int) - prev := make(map[point]point) +func (g *graph) bfs() bool { + queue := queue.New[point.Point]() + queue.Enqueue(g.start) + visited := make(map[point.Point]struct{}) for !queue.IsEmpty() { current := queue.Dequeue() - // we found a shortest path - if current == end { - return g.generatePath(start, end, prev), nil + if current == g.end { + return true } _, ok := visited[current] @@ -79,40 +76,21 @@ func (g *graph) dijkstra(start, end point) ([]point, error) { continue } - _, ok := costs[n] - if !ok { - costs[n] = math.MaxInt - } - - new_cost := costs[current] + 1 - if new_cost < costs[n] { - costs[n] = new_cost - prev[n] = current - queue.Enqueue(n) - } + queue.Enqueue(n) } } + log.Debugf("\n%s\n", g.debug(visited)) - return nil, errors.New("unable to find a shortest path") + return false } -func (g *graph) generatePath(start, end point, prev map[point]point) []point { - path := []point{end} - node := prev[end] - for node != start { - path = append(path, prev[node]) - node = prev[node] - } - return path -} - -func (g *graph) debug(path []point) string { +func (g *graph) debug(visited map[point.Point]struct{}) string { temp := slices.Clone(g.data) - for _, p := range path { + for p := range visited { if g.valueAt(p) == '#' { continue } - temp[p.y] = replaceAtIndex(temp[p.y], 'O', p.x) + temp[p.Y] = replaceAtIndex(temp[p.Y], 'O', p.X) } return strings.Join(temp, "\n") } diff --git a/day-18/internal/two/neighbours.go b/day-18/internal/two/neighbours.go index 0dde7cf..920f26f 100644 --- a/day-18/internal/two/neighbours.go +++ b/day-18/internal/two/neighbours.go @@ -1,10 +1,12 @@ package two -func neighbours(p point) [4]point { - return [4]point{ - {p.x, p.y - 1}, // N - {p.x + 1, p.y}, // E - {p.x, p.y + 1}, // S - {p.x - 1, p.y}, // W +import "github.com/onyx-and-iris/aoc2024/day-18/internal/point" + +func neighbours(p point.Point) [4]point.Point { + return [4]point.Point{ + {X: p.X, Y: p.Y - 1}, // N + {X: p.X + 1, Y: p.Y}, // E + {X: p.X, Y: p.Y + 1}, // S + {X: p.X - 1, Y: p.Y}, // W } } diff --git a/day-18/internal/two/point.go b/day-18/internal/two/point.go deleted file mode 100644 index eef5cd7..0000000 --- a/day-18/internal/two/point.go +++ /dev/null @@ -1,6 +0,0 @@ -package two - -type point struct { - x int - y int -} diff --git a/day-18/internal/two/solve.go b/day-18/internal/two/solve.go index 158ed9d..c0fd32b 100644 --- a/day-18/internal/two/solve.go +++ b/day-18/internal/two/solve.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/onyx-and-iris/aoc2024/day-18/internal/config" + "github.com/onyx-and-iris/aoc2024/day-18/internal/point" log "github.com/sirupsen/logrus" ) @@ -16,6 +17,7 @@ func Solve(buf []byte, config config.Config) (string, error) { } log.Debugf("start: %v end: %v", graph.start, graph.end) + log.Debug(corruptedCoords) indx := runUntilNoPath(graph, corruptedCoords, config) return fmt.Sprintf("%d,%d", corruptedCoords[indx][0], corruptedCoords[indx][1]), nil @@ -23,17 +25,15 @@ func Solve(buf []byte, config config.Config) (string, error) { func runUntilNoPath(graph *graph, corruptedCoords [][]int, config config.Config) int { for i, coords := range corruptedCoords[config.NumCorruptions+1:] { - nextCorruption := point{coords[0], coords[1]} + nextCorruption := point.Point{X: coords[0], Y: coords[1]} log.Debugf("adding corruption %v", nextCorruption) + graph.addCorruption(nextCorruption) - graph.addCorruption(coords) - path, err := graph.dijkstra(graph.start, graph.end) - if err != nil { - log.Debug(err) + ok := graph.bfs() + if !ok { return config.NumCorruptions + i + 1 } - log.Debugf("\n%s\n", graph.debug(path)) } return 0 }