From c6651d045a6fbef7147e14f63bd5f631b69739fa Mon Sep 17 00:00:00 2001 From: C4 Patino Date: Sat, 13 Dec 2025 11:51:25 -0600 Subject: [PATCH] feat: completed day 8 for advent of code 2025 --- 2025/cmd/root.go | 2 + 2025/day08/day08.go | 203 +++++++++++++++++++++++++++++++++++++++ 2025/day08/day08_test.go | 34 +++++++ 2025/day08/test.txt | 20 ++++ 4 files changed, 259 insertions(+) create mode 100755 2025/day08/day08.go create mode 100755 2025/day08/day08_test.go create mode 100755 2025/day08/test.txt diff --git a/2025/cmd/root.go b/2025/cmd/root.go index 15ebaf5..140ee44 100644 --- a/2025/cmd/root.go +++ b/2025/cmd/root.go @@ -16,6 +16,7 @@ import ( "cpatino.com/advent-of-code/2025/day05" "cpatino.com/advent-of-code/2025/day06" "cpatino.com/advent-of-code/2025/day07" + "cpatino.com/advent-of-code/2025/day08" ) var days = map[int]func(string) (any, any){ @@ -26,6 +27,7 @@ var days = map[int]func(string) (any, any){ 5: day05.Run, 6: day06.Run, 7: day07.Run, + 8: day08.Run, } var rootCmd = &cobra.Command{ diff --git a/2025/day08/day08.go b/2025/day08/day08.go new file mode 100755 index 0000000..747fe98 --- /dev/null +++ b/2025/day08/day08.go @@ -0,0 +1,203 @@ +package day08 + +import ( + "bufio" + "fmt" + "math" + "os" + "sort" +) + +type Point struct { + x, y, z int +} + +type Pair struct { + i, j int + dist float64 +} + +type UnionFind struct { + parent []int + size []int +} + +func newUnionFind(n int) *UnionFind { + uf := &UnionFind{ + parent: make([]int, n), + size: make([]int, n), + } + for i := range uf.parent { + uf.parent[i] = i + uf.size[i] = 1 + } + return uf +} + +func (uf *UnionFind) Find(x int) int { + if uf.parent[x] != x { + uf.parent[x] = uf.Find(uf.parent[x]) + } + return uf.parent[x] +} + +func (uf *UnionFind) Union(x, y int) { + xRoot := uf.Find(x) + yRoot := uf.Find(y) + if xRoot == yRoot { + return + } + if uf.size[xRoot] < uf.size[yRoot] { + uf.parent[xRoot] = yRoot + uf.size[yRoot] += uf.size[xRoot] + } else { + uf.parent[yRoot] = xRoot + uf.size[xRoot] += uf.size[yRoot] + } +} + +func (uf *UnionFind) ComponentSizes() map[int]int { + sizes := make(map[int]int) + for i := range uf.parent { + root := uf.Find(i) + sizes[root]++ + } + return sizes +} + +type PairHeap []Pair + +func (h PairHeap) Len() int { return len(h) } +func (h PairHeap) Less(i, j int) bool { + if h[i].dist != h[j].dist { + return h[i].dist < h[j].dist + } + if h[i].i != h[j].i { + return h[i].i < h[j].i + } + return h[i].j < h[j].j +} + +func (h PairHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *PairHeap) Push(x any) { *h = append(*h, x.(Pair)) } +func (h *PairHeap) Pop() any { + old := *h + n := len(old) + x := old[n-1] + *h = old[:n-1] + return x +} + +func Part1(points []Point, K int) int { + n := len(points) + var pairs []Pair + for i := 0; i < n; i++ { + for j := i + 1; j < n; j++ { + dx := float64(points[i].x - points[j].x) + dy := float64(points[i].y - points[j].y) + dz := float64(points[i].z - points[j].z) + dist := math.Sqrt(dx*dx + dy*dy + dz*dz) + pairs = append(pairs, Pair{i, j, dist}) + } + } + + sort.Slice(pairs, func(a, b int) bool { + if pairs[a].dist != pairs[b].dist { + return pairs[a].dist < pairs[b].dist + } + if pairs[a].i != pairs[b].i { + return pairs[a].i < pairs[b].i + } + return pairs[a].j < pairs[b].j + }) + + uf := newUnionFind(n) + for i := 0; i < K && i < len(pairs); i++ { + p := pairs[i] + uf.Union(p.i, p.j) + } + + sizes := uf.ComponentSizes() + var sizeList []int + for _, sz := range sizes { + sizeList = append(sizeList, sz) + } + + sort.Sort(sort.Reverse(sort.IntSlice(sizeList))) + if len(sizeList) < 3 { + return -1 + } + + return sizeList[0] * sizeList[1] * sizeList[2] +} + +func Part2(points []Point) int { + n := len(points) + var pairs []Pair + for i := 0; i < n; i++ { + for j := i + 1; j < n; j++ { + dx := float64(points[i].x - points[j].x) + dy := float64(points[i].y - points[j].y) + dz := float64(points[i].z - points[j].z) + dist := math.Sqrt(dx*dx + dy*dy + dz*dz) + pairs = append(pairs, Pair{i, j, dist}) + } + } + + sort.Slice(pairs, func(a, b int) bool { + if pairs[a].dist != pairs[b].dist { + return pairs[a].dist < pairs[b].dist + } + if pairs[a].i != pairs[b].i { + return pairs[a].i < pairs[b].i + } + return pairs[a].j < pairs[b].j + }) + + uf := newUnionFind(n) + merges := 0 + var lastPair Pair + for _, p := range pairs { + if uf.Find(p.i) != uf.Find(p.j) { + uf.Union(p.i, p.j) + merges++ + lastPair = p + if merges == n-1 { + break + } + } + } + return points[lastPair.i].x * points[lastPair.j].x +} + +func Run(filename string) (any, any) { + file, err := os.Open(filename) + if err != nil { + panic(err) + } + defer file.Close() + + var points []Point + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if line == "" { + continue + } + var x, y, z int + _, err := fmt.Sscanf(line, "%d,%d,%d", &x, &y, &z) + if err != nil { + panic("invalid input line: " + line) + } + points = append(points, Point{x, y, z}) + } + + if err := scanner.Err(); err != nil { + panic(err) + } + + K := 1000 + part1 := Part1(points, K) + part2 := Part2(points) + return part1, part2 +} diff --git a/2025/day08/day08_test.go b/2025/day08/day08_test.go new file mode 100755 index 0000000..dbf9e36 --- /dev/null +++ b/2025/day08/day08_test.go @@ -0,0 +1,34 @@ +package day08 + +import ( + "os" + "testing" +) + +type TestCase struct { + FileName string + Part1 any + Part2 any +} + +func TestDay08(t *testing.T) { + tests := []TestCase{ + {"test.txt", -1, 25272}, + {"input.txt", 164475, 169521198}, + } + + for _, test := range tests { + if _, err := os.Stat(test.FileName); os.IsNotExist(err) { + t.Fatalf("test file does not exist: %v", test.FileName) + } + + part1, part2 := Run(test.FileName) + if test.Part1 != nil && part1 != test.Part1 { + t.Fatalf("%s: unexpected part1:\nwant:\t%v\ngot:\t%v", test.FileName, test.Part1, part1) + } + + if test.Part2 != nil && part2 != test.Part2 { + t.Fatalf("%s: unexpected part2:\nwant:\t%v\ngot:\t%v", test.FileName, test.Part2, part2) + } + } +} diff --git a/2025/day08/test.txt b/2025/day08/test.txt new file mode 100755 index 0000000..e98a3b6 --- /dev/null +++ b/2025/day08/test.txt @@ -0,0 +1,20 @@ +162,817,812 +57,618,57 +906,360,560 +592,479,940 +352,342,300 +466,668,158 +542,29,236 +431,825,988 +739,650,466 +52,470,668 +216,146,977 +819,987,18 +117,168,530 +805,96,715 +346,949,466 +970,615,88 +941,993,340 +862,61,35 +984,92,344 +425,690,689