advent of code 2025 in ts and nix
1const file = await Bun.file("../../shared/08/input.txt").text();
2
3// Parse junction coordinates
4const junctions = file
5 .trim()
6 .split("\n")
7 .map((line) => {
8 const [x, y, z] = line.split(",").map(Number);
9 return { x, y, z };
10 });
11
12// Calculate distance between two junctions
13function distance(
14 a: { x: number; y: number; z: number },
15 b: { x: number; y: number; z: number }
16): number {
17 return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2 + (a.z - b.z) ** 2);
18}
19
20// Generate all pairs with distances
21const pairs = [];
22for (let i = 0; i < junctions.length; i++) {
23 for (let j = i + 1; j < junctions.length; j++) {
24 pairs.push({
25 i,
26 j,
27 distance: distance(junctions[i], junctions[j]),
28 });
29 }
30}
31
32// Sort by distance
33pairs.sort((a, b) => a.distance - b.distance);
34
35// Union-Find data structure
36class UnionFind {
37 parent: number[];
38 size: number[];
39
40 constructor(n: number) {
41 this.parent = Array.from({ length: n }, (_, i) => i);
42 this.size = Array(n).fill(1);
43 }
44
45 find(x: number): number {
46 if (this.parent[x] !== x) {
47 this.parent[x] = this.find(this.parent[x]);
48 }
49 return this.parent[x];
50 }
51
52 union(x: number, y: number): boolean {
53 const rootX = this.find(x);
54 const rootY = this.find(y);
55
56 if (rootX === rootY) return false;
57
58 if (this.size[rootX] < this.size[rootY]) {
59 this.parent[rootX] = rootY;
60 this.size[rootY] += this.size[rootX];
61 } else {
62 this.parent[rootY] = rootX;
63 this.size[rootX] += this.size[rootY];
64 }
65
66 return true;
67 }
68
69 getCircuitSizes(): number[] {
70 const circuits = new Map<number, number>();
71 for (let i = 0; i < this.parent.length; i++) {
72 const root = this.find(i);
73 circuits.set(root, this.size[root]);
74 }
75 return Array.from(circuits.values()).sort((a, b) => b - a);
76 }
77
78 getCircuitCount(): number {
79 const roots = new Set<number>();
80 for (let i = 0; i < this.parent.length; i++) {
81 roots.add(this.find(i));
82 }
83 return roots.size;
84 }
85}
86
87(() => {
88 // Part 1: After 1000 connections, product of top 3 circuit sizes
89 const uf = new UnionFind(junctions.length);
90
91 for (let i = 0; i < 1000; i++) {
92 uf.union(pairs[i].i, pairs[i].j);
93 }
94
95 const circuitSizes = uf.getCircuitSizes();
96 const top3 = circuitSizes.slice(0, 3);
97 const product = top3[0] * top3[1] * top3[2];
98
99 console.log("part 1:", product);
100})();
101
102(() => {
103 // Part 2: Find when all junctions form a single circuit
104 const uf = new UnionFind(junctions.length);
105
106 for (let i = 0; i < pairs.length; i++) {
107 uf.union(pairs[i].i, pairs[i].j);
108
109 if (uf.getCircuitCount() === 1) {
110 // Found the connection that unified everything
111 const pair = pairs[i];
112 const product = junctions[pair.i].x * junctions[pair.j].x;
113 console.log("part 2:", product);
114 break;
115 }
116 }
117})();