···
13
+
// Calculate all pairwise distances
14
+
function distance(a, b) {
15
+
return Math.sqrt((a.x - b.x) ** 2 + (a.y - b.y) ** 2 + (a.z - b.z) ** 2);
19
+
for (let i = 0; i < junctions.length; i++) {
20
+
for (let j = i + 1; j < junctions.length; j++) {
24
+
distance: distance(junctions[i], junctions[j]),
30
+
pairs.sort((a, b) => a.distance - b.distance);
32
+
// Union-Find for circuit tracking
35
+
this.parent = Array.from({ length: n }, (_, i) => i);
36
+
this.size = Array(n).fill(1);
40
+
if (this.parent[x] !== x) {
41
+
this.parent[x] = this.find(this.parent[x]);
43
+
return this.parent[x];
47
+
const rootX = this.find(x);
48
+
const rootY = this.find(y);
50
+
if (rootX === rootY) return false;
52
+
if (this.size[rootX] < this.size[rootY]) {
53
+
this.parent[rootX] = rootY;
54
+
this.size[rootY] += this.size[rootX];
56
+
this.parent[rootY] = rootX;
57
+
this.size[rootX] += this.size[rootY];
64
+
const circuits = new Map();
65
+
for (let i = 0; i < this.parent.length; i++) {
66
+
const root = this.find(i);
67
+
circuits.set(root, this.size[root]);
69
+
return Array.from(circuits.values()).sort((a, b) => b - a);
73
+
const roots = new Set();
74
+
for (let i = 0; i < this.parent.length; i++) {
75
+
roots.add(this.find(i));
81
+
// Build animation stages for Part 1 (first 1000 connections) and Part 2 (until single circuit)
83
+
const uf = new UnionFind(junctions.length);
84
+
const connections = [];
85
+
let part2ConnectionIndex = -1;
86
+
let part2Product = 0;
90
+
const circuitSizes = uf.getCircuitSizes();
91
+
const top3 = circuitSizes.slice(0, 3);
92
+
const product = top3.length >= 3 ? top3[0] * top3[1] * top3[2] : 0;
93
+
const circuitCount = uf.getCircuitCount();
96
+
connections: [...connections],
97
+
circuits: circuitCount,
98
+
largest: circuitSizes[0] || 1,
100
+
circuitSizes: [...circuitSizes],
101
+
part2Product: part2Product,
104
+
// Check if we've reached a single circuit (Part 2 complete)
105
+
if (circuitCount === 1 && part2ConnectionIndex === -1) {
106
+
part2ConnectionIndex = step - 1; // The previous connection completed it
107
+
if (part2ConnectionIndex >= 0) {
108
+
const lastPair = pairs[part2ConnectionIndex];
109
+
part2Product = junctions[lastPair.i].x * junctions[lastPair.j].x;
111
+
// Update the current stage with the part2Product
112
+
stages[stages.length - 1].part2Product = part2Product;
115
+
// Stop after reaching single circuit, or if we've exhausted pairs
116
+
if (circuitCount === 1 || step >= pairs.length) {
120
+
const pair = pairs[step];
121
+
uf.union(pair.i, pair.j);
122
+
connections.push(pair);
127
+
"Part 1: After 1000 connections, product =",
128
+
stages[Math.min(1000, stages.length - 1)].product,
131
+
"Part 2: Single circuit at connection",
132
+
part2ConnectionIndex,
const inputData = JSON.stringify(junctions);
138
+
const maxConnections = stages[stages.length - 1].connections.length;
const html = `<!DOCTYPE html>
···
<div class="control-row">
<label for="speed">Speed:</label>
<input type="range" id="speed" min="0" max="1000" value="900" step="5">
201
-
<span class="info" id="stepInfo">Step: 0 / 1000</span>
326
+
<span class="info" id="stepInfo">Step: 0 / ${stages.length - 1}</span>
<div class="legend-item"><div class="legend-box junction"></div> Isolated Junction (small)</div>
···
211
-
<div id="statsInfo">Circuits: ${junctions.length} | Largest: 0 | Product: 0</div>
336
+
<div id="statsInfo">Circuits: ${junctions.length} | Largest: 0 | Part 1: 0 | Part 2: 0</div>
<div style="margin-top: 5px; font-size: 11px;"><a href="../index.html">[Return to Index]</a></div>
···
return Array.from(circuits.values()).sort((a, b) => b - a);
425
+
getCircuitCount() {
426
+
const roots = new Set();
427
+
for (let i = 0; i < this.parent.length; i++) {
428
+
roots.add(this.find(i));
301
-
// Build animation stages (connections to make)
434
+
// Build animation stages for Part 1 (first 1000 connections) and Part 2 (until single circuit)
const uf = new UnionFind(junctions.length);
438
+
let part2ConnectionIndex = -1;
439
+
let part2Product = 0;
306
-
for (let step = 0; step <= 1000; step++) {
const circuitSizes = uf.getCircuitSizes();
const top3 = circuitSizes.slice(0, 3);
const product = top3.length >= 3 ? top3[0] * top3[1] * top3[2] : 0;
446
+
const circuitCount = uf.getCircuitCount();
connections: [...connections],
313
-
circuits: circuitSizes.length,
450
+
circuits: circuitCount,
largest: circuitSizes[0] || 1,
316
-
circuitSizes: [...circuitSizes]
453
+
circuitSizes: [...circuitSizes],
454
+
part2Product: part2Product
320
-
const pair = pairs[step];
321
-
uf.union(pair.i, pair.j);
322
-
connections.push(pair);
457
+
// Check if we've reached a single circuit (Part 2 complete)
458
+
if (circuitCount === 1 && part2ConnectionIndex === -1) {
459
+
part2ConnectionIndex = step - 1; // The previous connection completed it
460
+
if (part2ConnectionIndex >= 0) {
461
+
const lastPair = pairs[part2ConnectionIndex];
462
+
part2Product = junctions[lastPair.i].x * junctions[lastPair.j].x;
464
+
// Update the current stage with the part2Product
465
+
stages[stages.length - 1].part2Product = part2Product;
468
+
// Stop after reaching single circuit, or if we've exhausted pairs
469
+
if (circuitCount === 1 || step >= pairs.length) {
473
+
const pair = pairs[step];
474
+
uf.union(pair.i, pair.j);
475
+
connections.push(pair);
479
+
console.log('Part 1: After 1000 connections, product =', stages[Math.min(1000, stages.length - 1)].product);
480
+
console.log('Part 2: Single circuit at connection', part2ConnectionIndex, ', product =', part2Product);
const scene = new THREE.Scene();
···
385
-
// Connection lines - pre-render all cylinders
541
+
// Connection lines - pre-render all cylinders (enough to reach single circuit)
let connectionCylinders = [];
544
+
// Determine how many connections we need (until single circuit)
545
+
const maxConnections = Math.min(pairs.length, stages[stages.length - 1].connections.length);
// Pre-create all connection cylinders
389
-
console.log('Pre-rendering', pairs.slice(0, 1000).length, 'connections...');
390
-
pairs.slice(0, 1000).forEach((pair, idx) => {
548
+
console.log('Pre-rendering', maxConnections, 'connections...');
549
+
pairs.slice(0, maxConnections).forEach((pair, idx) => {
const j1 = junctions[pair.i];
const j2 = junctions[pair.j];
···
const stage = stages[currentStage];
536
-
stepInfo.textContent = \`Step: \${currentStage} / 1000\`;
537
-
statsInfo.textContent = \`Circuits: \${stage.circuits} | Largest: \${stage.largest} | Product: \${stage.product.toLocaleString()}\`;
695
+
stepInfo.textContent = \`Step: \${currentStage} / \${stages.length - 1}\`;
697
+
const part1Result = stages[Math.min(1000, stages.length - 1)].product;
698
+
const part2Result = stage.part2Product || 0;
700
+
statsInfo.textContent = \`Circuits: \${stage.circuits} | Largest: \${stage.largest} | Part 1: \${part1Result.toLocaleString()} | Part 2: \${part2Result.toLocaleString()}\`;
prevBtn.disabled = currentStage === 0;
nextBtn.disabled = currentStage === stages.length - 1;