advent of code 2025 in ts and nix

feat: add day 8 vis for part 1

dunkirk.sh cedcc3f8 da0877c7

verified
Changed files
+2227
nix
scripts
shared
ts
vis
+10
nix/08/solution.nix
···
···
+
let
+
input = builtins.readFile ../../shared/08/input.txt;
+
lines = builtins.filter (s: builtins.isString s && s != "") (builtins.split "\n" input);
+
+
part1 = 0;
+
part2 = 0;
+
+
in {
+
inherit part1 part2;
+
}
+5
scripts/generate-vis.ts
···
description:
"Watch tachyon beams split as they travel through a manifold. Each splitter (^) stops a beam and creates two new beams extending left and right.",
},
};
const dayCards = days
···
description:
"Watch tachyon beams split as they travel through a manifold. Each splitter (^) stops a beam and creates two new beams extending left and right.",
},
+
"08": {
+
title: "Playground Junction Boxes",
+
description:
+
"3D visualization of junction boxes being connected by their shortest distances. Watch circuits form as connections merge isolated boxes into larger groups.",
+
},
};
const dayCards = days
+1000
shared/08/input.txt
···
···
+
5161,22203,73169
+
66101,34794,41187
+
19740,85581,70637
+
64804,51256,2927
+
68224,49993,7867
+
9878,97392,46130
+
40001,51521,79544
+
45624,73253,74846
+
30230,58923,59531
+
84723,58102,86638
+
14537,44841,84567
+
41558,92494,89096
+
75012,1225,75685
+
77420,67581,11653
+
2365,26154,61974
+
80460,35718,17495
+
84603,85352,97745
+
85312,81113,25465
+
84808,39686,39315
+
78930,20627,23019
+
9138,22120,32917
+
43534,38414,62909
+
1852,53657,68936
+
42113,82762,91457
+
54027,40542,58053
+
82202,42280,72268
+
202,67735,80514
+
52305,854,45988
+
88343,55523,31987
+
28291,46179,65951
+
91364,63556,74551
+
86323,53379,5679
+
4978,97691,830
+
72221,98591,40848
+
76259,31452,38504
+
24278,31745,72816
+
43435,38188,12128
+
38931,44301,87982
+
12819,90012,60798
+
22440,63786,13427
+
72991,66943,5903
+
99694,22765,27658
+
14531,64099,3781
+
81830,53305,71039
+
16083,37712,94291
+
1861,15536,36482
+
49895,20278,79203
+
81296,28469,21142
+
18962,44916,45948
+
75556,98208,8719
+
17073,99830,64332
+
80061,73938,7885
+
84505,81534,49321
+
51314,28742,90563
+
45266,91838,54563
+
68720,31965,85325
+
17087,71197,81398
+
21515,19915,83771
+
98426,60158,81708
+
39695,27980,22169
+
74294,16001,70164
+
37834,77125,68205
+
50158,66031,33997
+
6228,61768,85916
+
18288,41150,22719
+
22002,47425,92208
+
56877,4935,3480
+
62885,9582,43220
+
40257,2330,33318
+
98759,96290,18372
+
73781,23958,3804
+
7471,82734,46760
+
35024,97965,71262
+
37867,65856,50384
+
52511,81222,52412
+
33482,59673,3263
+
5675,59568,35932
+
21251,43750,56819
+
96765,7988,87769
+
20307,50290,65243
+
17293,84764,12114
+
66302,92742,19412
+
72049,98072,97246
+
53960,10572,73353
+
17692,2175,43186
+
26181,26692,78570
+
6206,68914,3522
+
80114,23493,14095
+
35469,79858,91410
+
21464,22046,6372
+
39867,59222,96369
+
41647,17493,96062
+
21818,91894,75532
+
8309,91873,78553
+
211,75084,88402
+
70079,49640,88549
+
53250,64935,66594
+
59536,22170,30100
+
60861,99105,14003
+
61621,27840,90515
+
74639,29893,7322
+
84678,40440,94821
+
97113,95314,13072
+
68608,49658,91760
+
63585,86976,70868
+
80713,2187,45320
+
53966,90337,38684
+
12879,57544,67234
+
1808,55865,37081
+
29062,1242,56047
+
68536,82413,79663
+
82931,37875,80295
+
71837,70100,82856
+
8604,29732,15837
+
55168,22932,85866
+
7831,59139,11394
+
68246,95928,25366
+
5516,88931,66654
+
96479,58400,51809
+
21133,61075,29966
+
43770,13343,39028
+
75024,25103,58888
+
35728,97042,40407
+
93159,78375,73702
+
59258,10570,78625
+
58756,87652,98984
+
32438,36234,86197
+
42461,39846,29989
+
91102,74517,69917
+
24981,69124,48442
+
95106,21901,90494
+
52329,5337,48036
+
74828,37780,86916
+
73037,37561,82293
+
11261,5900,29591
+
47758,40284,70586
+
44437,88862,81499
+
2771,47188,65346
+
48737,73833,22256
+
6681,31926,14269
+
14890,4721,99568
+
74643,59728,83172
+
44018,65177,11311
+
28163,95996,55663
+
62284,4057,40282
+
85786,8606,16706
+
38301,35943,83825
+
93121,30446,83456
+
80709,93878,46043
+
25806,35135,61144
+
82648,87205,99228
+
81004,39106,48053
+
45717,30505,54773
+
97465,49038,15700
+
46560,20959,89092
+
41799,67704,577
+
36770,96608,79263
+
40028,55549,56718
+
14448,88161,97119
+
26854,84620,40111
+
70853,7432,76917
+
55200,24264,16287
+
59,67218,14635
+
6942,65638,37471
+
68640,68354,35571
+
40272,87947,27089
+
6692,88929,43004
+
21229,45052,37013
+
15926,71370,74945
+
92692,75869,34838
+
91181,95437,48530
+
18204,12940,45467
+
35323,15520,69239
+
82373,40019,86985
+
80756,54592,21710
+
48243,31541,44180
+
46122,84026,53779
+
39293,93745,62710
+
46031,21084,76780
+
18406,22463,33328
+
49421,94086,30128
+
76265,81888,38769
+
51061,44872,30360
+
11748,24035,63578
+
27664,31317,20723
+
41342,56333,80116
+
6524,33672,42019
+
36451,16429,91763
+
25211,84537,10563
+
57256,68807,87002
+
16992,17366,32886
+
2095,66214,26424
+
72798,44489,12613
+
72302,77566,22389
+
83839,80165,79968
+
11488,5510,666
+
13321,26484,91940
+
88598,20942,28033
+
45663,93458,8868
+
13632,60855,21950
+
69401,17820,15957
+
99693,49351,24797
+
76038,24933,31774
+
36063,8828,29548
+
91198,49648,72812
+
14950,49734,84990
+
29974,24256,43142
+
19381,8237,23391
+
22823,5267,23772
+
57408,109,81592
+
25135,3341,44930
+
22021,39757,75364
+
921,25399,58355
+
34100,75425,22714
+
49845,80698,15599
+
75078,43967,63211
+
33418,70727,81931
+
11894,93560,98772
+
18360,40040,11898
+
13341,57104,74958
+
20124,85072,36147
+
11466,57461,25681
+
4890,38197,35448
+
24857,93228,69880
+
50868,43259,83785
+
10058,67418,98282
+
14583,66434,53841
+
74869,52825,81807
+
17976,7604,55075
+
28345,58341,54910
+
23937,31696,20983
+
15299,65989,48101
+
85948,89595,74642
+
55386,58983,43994
+
99999,44512,37075
+
75074,15909,8571
+
86057,69748,62953
+
30817,82342,17104
+
90956,22036,54469
+
93486,72096,63425
+
21847,61753,27413
+
76878,83913,6736
+
47263,86404,11866
+
54473,77380,8133
+
43916,77261,44094
+
31411,73190,80946
+
57844,38767,42289
+
67832,36459,80545
+
50394,7272,47306
+
32819,25545,67025
+
69061,50499,87171
+
29321,3573,96393
+
790,54881,30567
+
10323,19294,61513
+
42390,48286,82826
+
48131,42651,2327
+
63796,43817,56908
+
28226,55100,69879
+
20961,95836,666
+
22399,63924,10001
+
8586,66790,40318
+
66927,1619,64174
+
34447,39223,88816
+
53211,28305,63761
+
59463,98390,3323
+
21005,38029,96138
+
18979,86778,11125
+
39421,55489,41546
+
66733,67487,21536
+
85741,27543,34114
+
54905,19440,27003
+
80920,97553,51524
+
64486,94319,16244
+
47427,88423,99263
+
94831,59526,29750
+
20612,19198,3428
+
36838,45561,55848
+
3739,24234,96805
+
30520,80842,25923
+
75905,4527,45717
+
57097,36462,29937
+
12449,12092,75382
+
16241,91132,18465
+
92421,32601,39642
+
94358,19855,92919
+
98029,17123,98277
+
62997,9369,62516
+
5743,97599,15596
+
55128,96754,63021
+
64749,29348,87579
+
50931,49963,16511
+
24104,10431,32239
+
85592,78853,55919
+
54462,92986,61923
+
82923,98099,69987
+
65451,96563,45754
+
56042,38226,27831
+
10024,90940,53047
+
51092,54380,11833
+
58088,87728,35943
+
71276,19838,71114
+
28525,42958,70906
+
88225,61386,12953
+
74256,4303,23200
+
49526,36042,15796
+
9153,94788,23665
+
80101,23495,50819
+
31569,685,20686
+
22964,44468,35471
+
70246,98432,61863
+
72794,47183,51421
+
6710,30845,79164
+
81930,58931,76876
+
60308,77291,46931
+
95213,70380,78860
+
39666,17476,7322
+
95063,1905,43670
+
54354,12416,76624
+
53740,3491,62481
+
12864,30769,52235
+
32045,63460,79567
+
75862,90991,84898
+
38669,63989,25022
+
7799,5513,46763
+
24068,39447,50344
+
36023,279,64133
+
5465,72858,7840
+
29160,24991,21515
+
59713,27231,3281
+
36164,90203,93847
+
75331,71291,86760
+
42565,52454,17080
+
6955,23878,44603
+
40854,37169,54460
+
5285,66356,26455
+
63058,94851,50037
+
12741,4910,77151
+
32824,27982,26924
+
47478,85766,35219
+
16457,86168,96792
+
83127,34305,62074
+
95548,14535,41035
+
88855,5223,94494
+
50146,163,79528
+
23338,33348,38377
+
4070,21412,15793
+
96775,52388,47999
+
12154,63542,15677
+
88504,99262,54679
+
12808,85236,5704
+
3906,14610,29712
+
32559,83568,27884
+
93026,38658,39179
+
14070,67679,15610
+
29713,12625,84667
+
6921,19610,86805
+
66462,57191,80287
+
60537,75661,4167
+
38067,9388,24090
+
92022,81040,60306
+
54484,9192,84268
+
36791,90762,60181
+
75947,15733,50393
+
16807,86935,85406
+
8328,60108,63149
+
71295,14068,96601
+
80613,26940,67177
+
89681,62762,34136
+
10217,93743,30456
+
73059,67804,71175
+
9005,74733,45169
+
43845,10522,61274
+
56005,15428,11649
+
83175,17043,59977
+
58911,36157,73078
+
18088,79910,55693
+
81267,64100,26580
+
56896,25358,96591
+
65687,86996,70012
+
77409,95703,52067
+
31023,70310,62432
+
47952,90510,54558
+
68213,85589,46380
+
58332,90985,35281
+
54333,45220,15414
+
94029,8669,19406
+
14313,90201,57318
+
93133,35598,4739
+
57923,86809,49393
+
96319,38176,37542
+
88108,46715,16554
+
57259,86355,83582
+
92724,64865,59543
+
21703,12027,77429
+
82537,84727,10231
+
69466,70604,11687
+
25405,34281,72216
+
13429,28547,21532
+
50561,60774,36501
+
96078,21610,59197
+
75502,7393,90387
+
84406,14971,69869
+
63056,23127,75447
+
2987,35313,26884
+
75976,38124,62378
+
50678,51910,64734
+
92897,9414,78851
+
12385,9019,52962
+
49969,73736,13654
+
85171,97718,94935
+
69860,46369,35285
+
48996,47871,45331
+
34138,70912,14473
+
81860,631,53276
+
94452,580,73549
+
62698,2127,79924
+
25326,51241,35453
+
96322,9704,66070
+
18980,34897,88148
+
43690,93420,6598
+
74205,53286,67674
+
3254,30109,93966
+
57779,91666,11952
+
72134,40940,44488
+
99873,83052,1126
+
97309,37450,91956
+
55829,40306,91718
+
78390,46731,20057
+
91883,47744,85866
+
78125,3512,78114
+
54410,61872,96284
+
11630,4634,48245
+
583,97186,63568
+
50298,62275,77450
+
46247,70570,70214
+
94826,69412,5578
+
78568,78523,95664
+
36379,91084,3661
+
11137,95017,38825
+
71379,44935,23389
+
22116,10182,19393
+
26631,14386,8913
+
90244,18267,36040
+
53393,4365,99048
+
4079,95272,99036
+
59709,84380,53915
+
65569,42790,12489
+
61765,46485,97361
+
82601,59377,69738
+
59108,17968,28167
+
6237,47107,46872
+
93572,74166,87007
+
92142,17034,81637
+
11605,97185,26640
+
69469,6432,23789
+
69985,80520,26503
+
65480,41448,77875
+
16205,81692,74363
+
69176,87344,11992
+
57385,71058,8987
+
23453,46306,89249
+
9957,58219,86932
+
64935,2339,20205
+
31980,85097,77988
+
90690,42666,7944
+
74914,20830,82559
+
68530,35265,76156
+
82762,4040,99880
+
23472,30526,42857
+
87759,55861,81598
+
1957,63841,57021
+
44944,18845,90230
+
54251,38708,38260
+
8870,753,71611
+
81423,5959,45753
+
94142,43274,37696
+
86379,70304,7891
+
44515,72191,1194
+
6205,77653,33542
+
92967,18453,48577
+
7678,67117,32246
+
32575,51952,80296
+
22384,73600,14490
+
52711,30082,25106
+
57480,42599,8822
+
43114,59009,65318
+
15762,17130,11220
+
70564,47248,83517
+
2121,75358,38760
+
35749,33046,52462
+
75238,37380,60124
+
96192,97611,49292
+
97758,97953,72104
+
58423,34477,87667
+
38902,67350,99834
+
42943,30562,94103
+
87924,51390,54745
+
81506,4822,81958
+
53178,12282,19647
+
85139,63746,27671
+
81487,89072,36336
+
6253,25748,86215
+
48027,49098,56556
+
43576,68684,55894
+
18254,6493,84642
+
52909,84771,72402
+
65560,53513,82375
+
81009,57728,62936
+
3688,11379,22586
+
26035,72184,18309
+
35242,7329,55693
+
71894,92384,38569
+
14615,55929,26218
+
26019,15150,17171
+
66669,33277,40758
+
24931,4862,27660
+
75483,86544,67579
+
5083,12873,65276
+
1313,87524,29121
+
286,17310,28052
+
31431,78066,80220
+
97016,30080,85033
+
55036,47034,83591
+
29322,15017,27875
+
74140,43294,70313
+
11167,98589,17524
+
98795,81059,18560
+
12125,50747,94693
+
29937,96225,94110
+
35740,63048,74201
+
60963,97471,54331
+
25085,26113,87315
+
79011,55630,79771
+
42718,62834,7988
+
66051,65078,5058
+
69214,22915,92689
+
49908,23340,39297
+
69887,84399,92649
+
78657,85796,70300
+
93483,74898,95070
+
46907,50571,41782
+
70877,41047,48729
+
41524,75771,99337
+
53466,47701,38633
+
12762,44499,22350
+
44688,9488,25293
+
71430,21562,52514
+
66810,36914,96406
+
99100,39368,50027
+
3160,44565,15249
+
63424,89275,68274
+
97623,53617,59825
+
2322,2086,37512
+
40230,51063,69967
+
13255,99386,61085
+
76434,51331,71427
+
20341,71310,18982
+
43964,43210,37969
+
36453,34957,99037
+
33461,99489,89885
+
47027,40641,91396
+
90242,14775,6617
+
77782,57333,73540
+
98828,75818,59586
+
25833,94734,11049
+
23932,92894,49069
+
82190,13166,17162
+
96342,7060,51505
+
31207,39374,141
+
63816,76588,17759
+
59091,83198,27627
+
15102,8698,84051
+
13417,49225,56704
+
9458,71311,45454
+
46120,10697,75585
+
78524,87896,27662
+
99544,44501,37210
+
77089,31087,11164
+
19136,55656,28658
+
96836,89792,77233
+
1573,69768,70774
+
34548,4159,64615
+
97621,88974,97672
+
16412,95340,19485
+
6842,70744,21696
+
27216,62222,95880
+
54156,26578,79466
+
40425,6222,41375
+
96958,27155,62281
+
69478,3232,61554
+
12529,31863,2982
+
31439,47831,1305
+
27766,33929,62205
+
28107,15467,47852
+
82990,80468,96872
+
66036,57413,76555
+
12420,63388,22307
+
55157,75636,36508
+
51115,58706,29691
+
49857,40192,53190
+
51028,36248,72849
+
96395,20052,57260
+
94013,71425,47753
+
46776,76728,4429
+
52826,60416,51781
+
82089,73840,90552
+
67515,19072,62369
+
93647,14133,12132
+
86372,9775,77886
+
65032,18651,36941
+
29189,6126,46728
+
91647,15409,23234
+
11242,36242,45807
+
53248,16864,60107
+
71327,27046,9222
+
50402,75876,44510
+
33404,96699,84855
+
45579,83811,17786
+
39497,27697,84681
+
36596,86508,9225
+
58008,26663,48254
+
44342,7399,9660
+
1899,94854,78032
+
55241,41264,65612
+
14171,61023,83439
+
15896,85118,19571
+
26262,92097,60999
+
90827,51591,55977
+
2986,30523,93726
+
46290,64597,98545
+
69550,43907,46248
+
14666,65895,65914
+
83466,3766,45688
+
92475,98128,98120
+
14468,1300,83791
+
42359,56603,13864
+
1998,87292,30935
+
30636,21447,4608
+
10281,80457,60591
+
5927,61652,33198
+
84792,90194,23668
+
21375,39200,63981
+
26148,2563,59453
+
3878,71863,32303
+
24688,40615,21628
+
39305,95887,5211
+
77164,28994,894
+
60900,79225,35865
+
83313,55862,45365
+
93289,62769,33883
+
78270,54992,59444
+
78875,24007,44581
+
6586,98225,43721
+
7609,14475,55643
+
52655,19354,1554
+
90896,48830,55935
+
79908,38414,98854
+
56763,69447,14243
+
20261,3353,28051
+
48847,25039,69183
+
73095,27262,96065
+
83789,46377,66387
+
28015,18283,30259
+
92510,84207,68258
+
68858,44507,25847
+
51411,84735,80122
+
24991,5280,32444
+
47479,24448,5575
+
9688,93891,63995
+
93516,32229,95595
+
49792,8956,75349
+
82466,2659,42943
+
84831,76883,60572
+
17885,26212,32434
+
72426,43039,45242
+
38540,73027,74988
+
75250,83395,25528
+
60282,63543,61072
+
16881,25850,35010
+
4519,87097,63686
+
47418,98765,14632
+
82891,70986,16303
+
1366,54258,91108
+
46829,42539,81531
+
54600,7812,3535
+
7137,68680,1136
+
88358,8124,599
+
69247,88524,96202
+
92311,35169,68307
+
91805,96856,32492
+
27952,60153,4739
+
30620,3652,43088
+
73324,88130,7501
+
86253,9120,62975
+
85991,90883,11260
+
26466,18263,33451
+
39237,61569,79497
+
37689,90031,38342
+
34696,18264,9226
+
45350,47447,78459
+
62677,45853,74744
+
57411,67900,45868
+
7048,66892,23560
+
87734,35737,27665
+
61109,48318,39804
+
36411,27,28578
+
6227,24921,9488
+
53477,26195,86182
+
77449,71240,84188
+
38630,98127,69702
+
99055,60171,99999
+
83349,97372,85844
+
39341,86407,75472
+
47680,96358,89076
+
2527,71367,872
+
30127,11915,92875
+
27824,89266,39931
+
38712,86498,96990
+
81776,27828,54759
+
63372,15940,11399
+
6071,66057,84756
+
11294,44057,62574
+
35475,49648,234
+
24252,10872,63749
+
63614,91455,75505
+
98426,38129,43879
+
31629,13292,31049
+
17440,64268,44872
+
55257,18709,72695
+
96667,64655,65603
+
16652,52245,63140
+
54593,16042,75859
+
62373,68010,91287
+
14761,50102,47525
+
48995,93258,52452
+
35039,36326,85135
+
53151,19858,18208
+
66066,14395,2972
+
69065,88336,55024
+
94201,5660,82690
+
16758,45854,30312
+
86733,56045,24153
+
60422,75296,63216
+
55152,16869,38967
+
40203,10071,52315
+
24979,79489,22106
+
62418,36395,28813
+
36002,25787,36541
+
5398,67134,19608
+
45567,29666,99903
+
10198,53619,78720
+
48266,94275,32975
+
44723,73473,86109
+
71654,10515,46265
+
96533,99156,95256
+
20173,16315,46171
+
55137,67991,2808
+
3277,59718,20458
+
24998,86919,72481
+
31496,67973,16417
+
96712,1737,48781
+
74575,64801,96462
+
87339,2057,43083
+
47263,89361,36045
+
45953,81799,84544
+
85626,35456,62954
+
69021,66867,67040
+
68578,93277,41488
+
2494,14008,27502
+
94737,79155,20947
+
8300,95363,14661
+
4261,1566,36906
+
61289,70259,4340
+
12951,58498,60371
+
97025,16001,79457
+
98406,60266,21369
+
23175,202,97114
+
63765,71722,25127
+
80262,89066,48532
+
10178,50750,60284
+
73012,51122,73821
+
71962,85888,29954
+
49604,27436,3498
+
33824,41959,18841
+
65525,44351,33838
+
19647,44807,75944
+
18365,17396,48109
+
12342,62941,32471
+
19433,88248,31068
+
53830,61194,20751
+
50327,80044,70438
+
47479,82735,53545
+
86533,36374,88149
+
76215,10963,7959
+
95856,68203,69083
+
44406,82301,90909
+
75945,41115,32836
+
75323,64386,3301
+
76595,43267,62186
+
71018,80419,60598
+
36152,87027,46004
+
82895,60201,73317
+
21221,20024,32799
+
30839,36544,19137
+
50665,7076,16737
+
85144,25721,53984
+
77746,44861,87239
+
29612,18252,73085
+
63340,8179,74011
+
94270,58535,51866
+
63687,25565,45329
+
42012,17325,65275
+
79074,21032,39506
+
97103,92135,77620
+
42475,76269,40353
+
79254,74928,93712
+
24498,82815,95878
+
24267,44573,4849
+
80452,22788,84351
+
86115,97837,17072
+
6138,63056,38463
+
21903,56589,68556
+
14741,63246,33703
+
22351,52367,5528
+
53539,47613,94853
+
44785,38786,14171
+
88087,94500,56183
+
73700,2726,3842
+
16250,73010,1209
+
55705,14057,73268
+
59932,40685,24530
+
92494,20475,37609
+
91863,64924,42834
+
46489,61374,49425
+
94363,39872,55202
+
13410,98087,45172
+
22051,11324,45962
+
44849,25662,88491
+
75769,63796,1104
+
65563,73886,19426
+
71046,47690,95322
+
74551,83083,69651
+
8615,4078,32306
+
77226,21601,16840
+
92787,80265,48396
+
65273,45058,4977
+
42226,7234,80530
+
32766,81532,34163
+
83798,3241,84369
+
38469,67001,76815
+
1894,88267,7920
+
45806,45132,89732
+
30945,91180,87272
+
1006,55022,65523
+
46291,77146,7658
+
60054,55561,86698
+
19915,80670,65432
+
56270,14003,18444
+
30490,99328,26538
+
37477,78725,5663
+
85532,9745,63388
+
57982,50202,93462
+
15320,29847,5357
+
49269,86040,48487
+
57114,68294,82371
+
95011,61493,60676
+
32850,78313,74952
+
12425,24856,59986
+
61739,94986,51353
+
25916,38678,93921
+
14719,91750,8822
+
70751,25157,31902
+
17034,9440,41006
+
18664,82908,58921
+
36354,22416,73832
+
64615,82081,20657
+
2180,53837,43929
+
71746,26827,47862
+
35313,46818,29867
+
63995,83312,62542
+
26389,18027,38909
+
89290,58554,65877
+
72755,9306,67463
+
34796,76540,91702
+
28031,92356,38544
+
24223,56569,15528
+
76978,72802,49103
+
33532,12892,20891
+
4541,85088,8174
+
1527,12502,12337
+
15927,71127,5248
+
96299,18029,68425
+
52808,90723,37783
+
92606,60196,77969
+
55899,73686,52560
+
23919,13265,86545
+
58996,69280,19651
+
51013,16113,1727
+
71789,30209,56287
+
71296,92980,91882
+
33408,92160,76319
+
42048,10919,83857
+
28792,74297,86478
+
5389,75937,81049
+
79159,44718,95910
+
77812,6109,6903
+
11429,2081,46678
+
28728,57702,40446
+
85490,69973,11074
+
18985,5870,9670
+
41900,64465,86223
+
64031,20749,69030
+
19968,63958,89442
+
7769,52832,45916
+
95865,7808,98612
+
39867,62263,12449
+
83497,47866,75410
+
82557,53917,16242
+
9807,26078,60255
+
15042,22029,69166
+
30024,51812,46565
+
92338,60566,9364
+
57715,22310,11197
+
97140,46213,37849
+
74647,55761,78801
+
6299,96259,38989
+
40357,37111,41316
+
30968,92364,46696
+
76414,51273,99009
+
7118,39225,38640
+
17737,14009,58457
+
39944,3341,30639
+
84410,19567,41648
+
15931,44298,65121
+
79307,94793,80987
+
952,58766,5923
+
69268,84830,19869
+
51994,80483,45526
+
32711,76202,36436
+
71893,59114,79883
+
99066,65336,68361
+
41506,31715,45108
+
82661,73919,50362
+
86802,61113,87896
+
80840,25948,71017
+
37009,56980,26364
+
75120,7694,92969
+
64429,15686,26000
+
46498,72376,35606
+
86689,27004,55646
+
4210,73749,54409
+
98698,23473,57220
+
10744,80544,68264
+
87968,61897,73680
+
52729,8385,35266
+
18864,31204,87105
+
86199,81893,5053
+
24062,50963,68619
+
61890,58242,84398
+
20147,67830,14430
+
98776,33533,40129
+
12323,27694,64221
+
72068,17313,20441
+
7514,65746,67963
+
52487,28248,54826
+
71958,4904,93581
+
98092,16710,97076
+
54744,77145,9758
+
44623,43540,95666
+
9774,88326,80075
+
12941,63893,95040
+
94286,36626,59162
+
51358,17484,86901
+
40056,64515,77119
+
87783,80188,43564
+
98201,84610,75208
+
61098,97658,6479
+
86959,5239,38368
+
73286,38785,89091
+
23913,59499,26416
+
25602,57654,72854
+
63260,85932,86084
+
13537,48315,73193
+
19335,11368,43913
+
83423,60937,37261
+
9313,47305,36979
+
723,518,67639
+
46040,85305,22039
+
31475,33436,47686
+
31640,79385,76978
+
27354,39004,31931
+
62329,20080,37076
+
81285,85346,48141
+
12914,98357,15867
+
97984,34251,11049
+
12225,26767,39580
+
88412,31758,79197
+
14614,36309,35051
+
84857,60361,669
+
51578,50383,82234
+11
ts/08/index.ts
···
···
+
const file = await Bun.file("../../shared/08/input.txt").text();
+
+
(() => {
+
// Part 1
+
console.log("part 1:", 0);
+
})();
+
+
(() => {
+
// Part 2
+
console.log("part 2:", 0);
+
})();
+609
vis/08/generate.ts
···
···
+
const scriptDir = import.meta.dir;
+
const file = await Bun.file(`${scriptDir}/../../shared/08/input.txt`).text();
+
+
// Parse junction coordinates
+
const junctions = file
+
.trim()
+
.split("\n")
+
.map((line) => {
+
const [x, y, z] = line.split(",").map(Number);
+
return { x, y, z };
+
});
+
+
const inputData = JSON.stringify(junctions);
+
+
const html = `<!DOCTYPE html>
+
<html lang="en">
+
<head>
+
<meta charset="UTF-8">
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
<title>AoC 2025 Day 8 - Playground Junction Boxes</title>
+
<style>
+
* {
+
box-sizing: border-box;
+
margin: 0;
+
padding: 0;
+
}
+
body {
+
background: #1e1e2e;
+
color: #cdd6f4;
+
font-family: "Source Code Pro", monospace;
+
font-size: 14pt;
+
font-weight: 300;
+
overflow: hidden;
+
margin: 0;
+
padding: 0;
+
}
+
#container {
+
width: 100vw;
+
height: 100vh;
+
position: relative;
+
}
+
#canvas {
+
width: 100%;
+
height: 100%;
+
display: block;
+
position: absolute;
+
top: 0;
+
left: 0;
+
z-index: 0;
+
}
+
.ui {
+
position: absolute;
+
top: 0;
+
left: 0;
+
right: 0;
+
padding: 20px;
+
pointer-events: none;
+
z-index: 1;
+
}
+
.ui > * {
+
pointer-events: auto;
+
}
+
h1 {
+
color: #a6e3a1;
+
text-shadow: 0 0 2px #a6e3a1, 0 0 5px #a6e3a1;
+
margin-bottom: 10px;
+
font-size: 1em;
+
font-weight: normal;
+
text-align: center;
+
}
+
.controls {
+
background: rgba(17, 17, 27, 0.9);
+
border: 1px solid #313244;
+
padding: 15px;
+
margin: 15px auto;
+
max-width: 800px;
+
border-radius: 4px;
+
}
+
.control-row {
+
display: flex;
+
gap: 15px;
+
align-items: center;
+
margin-bottom: 10px;
+
flex-wrap: wrap;
+
justify-content: center;
+
}
+
.control-row:last-child {
+
margin-bottom: 0;
+
}
+
button {
+
background: #11111b;
+
color: #a6e3a1;
+
border: 1px solid #313244;
+
padding: 8px 16px;
+
cursor: pointer;
+
font-family: inherit;
+
font-size: 14px;
+
border-radius: 3px;
+
}
+
button:hover {
+
background: #181825;
+
}
+
button:disabled {
+
opacity: 0.5;
+
cursor: not-allowed;
+
}
+
.info {
+
color: #f9e2af;
+
font-size: 14px;
+
}
+
.stats {
+
background: rgba(17, 17, 27, 0.9);
+
border: 1px solid #313244;
+
padding: 10px 15px;
+
margin: 0 auto;
+
max-width: 800px;
+
border-radius: 4px;
+
text-align: center;
+
font-size: 13px;
+
color: #a6adc8;
+
}
+
.legend {
+
display: flex;
+
gap: 15px;
+
margin-top: 10px;
+
flex-wrap: wrap;
+
justify-content: center;
+
}
+
.legend-item {
+
display: flex;
+
align-items: center;
+
gap: 6px;
+
font-size: 12px;
+
color: #a6adc8;
+
}
+
.legend-box {
+
width: 12px;
+
height: 12px;
+
border-radius: 50%;
+
}
+
.legend-box.junction { background: #89b4fa; }
+
.legend-box.connection { background: #a6e3a1; }
+
.legend-box.circuit { background: #f9e2af; }
+
input[type="range"] {
+
-webkit-appearance: none;
+
appearance: none;
+
width: 120px;
+
height: 6px;
+
background: #313244;
+
outline: none;
+
border: 1px solid #313244;
+
}
+
input[type="range"]::-webkit-slider-thumb {
+
-webkit-appearance: none;
+
appearance: none;
+
width: 16px;
+
height: 16px;
+
background: #a6e3a1;
+
cursor: pointer;
+
border: 1px solid #313244;
+
border-radius: 50%;
+
}
+
input[type="range"]::-moz-range-thumb {
+
width: 16px;
+
height: 16px;
+
background: #a6e3a1;
+
cursor: pointer;
+
border: 1px solid #313244;
+
border-radius: 50%;
+
}
+
label {
+
color: #a6adc8;
+
font-size: 13px;
+
}
+
a {
+
text-decoration: none;
+
color: #a6e3a1;
+
outline: 0;
+
}
+
a:hover, a:focus {
+
text-decoration: underline;
+
}
+
</style>
+
</head>
+
<body>
+
<div id="container">
+
<canvas id="canvas"></canvas>
+
<div class="ui">
+
<h1>AoC 2025 Day 8 - Playground Junction Boxes</h1>
+
+
<div class="controls">
+
<div class="control-row">
+
<button id="prev">← Previous</button>
+
<button id="play">▶ Play</button>
+
<button id="next">Next →</button>
+
<button id="reset">↺ Reset</button>
+
</div>
+
<div class="control-row">
+
<label for="speed">Speed:</label>
+
<input type="range" id="speed" min="0" max="1000" value="900" step="5">
+
<span class="info" id="stepInfo">Step: 0 / 1000</span>
+
</div>
+
<div class="legend">
+
<div class="legend-item"><div class="legend-box junction"></div> Isolated Junction (small)</div>
+
<div class="legend-item"><div class="legend-box connection"></div> Connected Junction (large)</div>
+
<div class="legend-item"><div class="legend-box circuit"></div> Circuit (color-coded)</div>
+
</div>
+
</div>
+
+
<div class="stats">
+
<div id="statsInfo">Circuits: ${junctions.length} | Largest: 0 | Product: 0</div>
+
<div style="margin-top: 5px; font-size: 11px;"><a href="../index.html">[Return to Index]</a></div>
+
</div>
+
</div>
+
</div>
+
+
<script type="importmap">
+
{
+
"imports": {
+
"three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
+
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
+
}
+
}
+
</script>
+
+
<script type="module">
+
import * as THREE from 'three';
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+
+
// Input data embedded
+
const junctions = ${inputData};
+
+
// Normalize coordinates to fit in view
+
const maxCoord = Math.max(...junctions.flatMap(j => [j.x, j.y, j.z]));
+
const scale = 20 / maxCoord;
+
+
// Calculate all pairwise distances
+
function distance(a, b) {
+
return Math.sqrt(
+
(a.x - b.x) ** 2 +
+
(a.y - b.y) ** 2 +
+
(a.z - b.z) ** 2
+
);
+
}
+
+
const pairs = [];
+
for (let i = 0; i < junctions.length; i++) {
+
for (let j = i + 1; j < junctions.length; j++) {
+
pairs.push({
+
i,
+
j,
+
distance: distance(junctions[i], junctions[j])
+
});
+
}
+
}
+
+
// Sort by distance
+
pairs.sort((a, b) => a.distance - b.distance);
+
+
// Union-Find for circuit tracking
+
class UnionFind {
+
constructor(n) {
+
this.parent = Array.from({ length: n }, (_, i) => i);
+
this.size = Array(n).fill(1);
+
}
+
+
find(x) {
+
if (this.parent[x] !== x) {
+
this.parent[x] = this.find(this.parent[x]);
+
}
+
return this.parent[x];
+
}
+
+
union(x, y) {
+
const rootX = this.find(x);
+
const rootY = this.find(y);
+
+
if (rootX === rootY) return false;
+
+
if (this.size[rootX] < this.size[rootY]) {
+
this.parent[rootX] = rootY;
+
this.size[rootY] += this.size[rootX];
+
} else {
+
this.parent[rootY] = rootX;
+
this.size[rootX] += this.size[rootY];
+
}
+
+
return true;
+
}
+
+
getCircuitSizes() {
+
const circuits = new Map();
+
for (let i = 0; i < this.parent.length; i++) {
+
const root = this.find(i);
+
circuits.set(root, this.size[root]);
+
}
+
return Array.from(circuits.values()).sort((a, b) => b - a);
+
}
+
}
+
+
// Build animation stages (connections to make)
+
const stages = [];
+
const uf = new UnionFind(junctions.length);
+
const connections = [];
+
+
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;
+
+
stages.push({
+
connections: [...connections],
+
circuits: circuitSizes.length,
+
largest: circuitSizes[0] || 1,
+
product,
+
circuitSizes: [...circuitSizes]
+
});
+
+
if (step < 1000) {
+
const pair = pairs[step];
+
uf.union(pair.i, pair.j);
+
connections.push(pair);
+
}
+
}
+
+
// Three.js setup
+
const scene = new THREE.Scene();
+
scene.background = new THREE.Color(0x0d0d15);
+
+
const camera = new THREE.PerspectiveCamera(
+
75,
+
window.innerWidth / window.innerHeight,
+
0.1,
+
1000
+
);
+
camera.position.set(15, 15, 15);
+
+
const renderer = new THREE.WebGLRenderer({
+
canvas: document.getElementById('canvas'),
+
antialias: true
+
});
+
renderer.setSize(window.innerWidth, window.innerHeight);
+
renderer.setPixelRatio(window.devicePixelRatio);
+
+
const controls = new OrbitControls(camera, renderer.domElement);
+
controls.enableDamping = true;
+
controls.dampingFactor = 0.05;
+
controls.target.set(0, 3, 0); // Shift the view down by 3 units
+
controls.autoRotate = true;
+
controls.autoRotateSpeed = 0.5; // Slow rotation (0.5 degrees per frame at 60fps = 30 seconds per rotation)
+
controls.update();
+
+
// Lighting
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
+
scene.add(ambientLight);
+
+
const pointLight1 = new THREE.PointLight(0xffffff, 0.6);
+
pointLight1.position.set(10, 10, 10);
+
scene.add(pointLight1);
+
+
const pointLight2 = new THREE.PointLight(0xffffff, 0.4);
+
pointLight2.position.set(-10, -10, -10);
+
scene.add(pointLight2);
+
+
// Create junction boxes (smaller)
+
const junctionGeometry = new THREE.SphereGeometry(0.1, 12, 12);
+
const junctionMaterial = new THREE.MeshPhongMaterial({
+
color: 0x89b4fa,
+
emissive: 0x89b4fa,
+
emissiveIntensity: 0.2
+
});
+
+
const junctionMeshes = junctions.map((j, idx) => {
+
const mesh = new THREE.Mesh(junctionGeometry, junctionMaterial);
+
mesh.position.set(
+
(j.x * scale) - 10,
+
(j.y * scale) - 10,
+
(j.z * scale) - 10
+
);
+
mesh.userData.index = idx;
+
scene.add(mesh);
+
return mesh;
+
});
+
+
// Connection lines - pre-render all cylinders
+
let connectionCylinders = [];
+
+
// Pre-create all connection cylinders
+
console.log('Pre-rendering', pairs.slice(0, 1000).length, 'connections...');
+
pairs.slice(0, 1000).forEach((pair, idx) => {
+
const j1 = junctions[pair.i];
+
const j2 = junctions[pair.j];
+
+
const p1 = new THREE.Vector3(
+
(j1.x * scale) - 10,
+
(j1.y * scale) - 10,
+
(j1.z * scale) - 10
+
);
+
const p2 = new THREE.Vector3(
+
(j2.x * scale) - 10,
+
(j2.y * scale) - 10,
+
(j2.z * scale) - 10
+
);
+
+
// Create cylinder between points
+
const direction = new THREE.Vector3().subVectors(p2, p1);
+
const length = direction.length();
+
const cylinderGeometry = new THREE.CylinderGeometry(0.015, 0.015, length, 4);
+
const cylinderMaterial = new THREE.MeshBasicMaterial({
+
transparent: true,
+
opacity: 0.8
+
});
+
+
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);
+
+
// Position at midpoint
+
cylinder.position.copy(p1).add(direction.multiplyScalar(0.5));
+
+
// Orient cylinder to connect the two points
+
cylinder.quaternion.setFromUnitVectors(
+
new THREE.Vector3(0, 1, 0),
+
direction.normalize()
+
);
+
+
// Initially invisible
+
cylinder.visible = false;
+
cylinder.userData.pairIndex = idx;
+
cylinder.userData.i = pair.i;
+
cylinder.userData.j = pair.j;
+
+
scene.add(cylinder);
+
connectionCylinders.push(cylinder);
+
});
+
console.log('Pre-rendering complete!');
+
+
// Pre-generate stable colors for each junction based on their index
+
const junctionColors = [];
+
const hues = [];
+
for (let i = 0; i < junctions.length; i++) {
+
const hue = (i * 137.508) % 360; // Golden angle for good distribution
+
junctionColors.push(new THREE.Color().setHSL(hue / 360, 0.8, 0.55));
+
}
+
+
function updateConnections(stage) {
+
const numConnections = stage.connections.length;
+
+
if (numConnections === 0) {
+
// No connections yet - make all junctions small and hide all cylinders
+
junctionMeshes.forEach(mesh => {
+
mesh.scale.set(0.33, 0.33, 0.33);
+
mesh.material = new THREE.MeshPhongMaterial({
+
color: 0x89b4fa,
+
emissive: 0x89b4fa,
+
emissiveIntensity: 0.2
+
});
+
});
+
connectionCylinders.forEach(cyl => cyl.visible = false);
+
return;
+
}
+
+
// Color junctions by circuit - use lowest junction index as stable color
+
const uf = new UnionFind(junctions.length);
+
stage.connections.forEach(conn => uf.union(conn.i, conn.j));
+
+
// Map each root to the lowest junction index in that circuit for stable colors
+
const circuitColors = new Map();
+
for (let i = 0; i < junctions.length; i++) {
+
const root = uf.find(i);
+
if (!circuitColors.has(root)) {
+
// Find the lowest index in this circuit
+
let lowestInCircuit = i;
+
for (let j = 0; j < i; j++) {
+
if (uf.find(j) === root) {
+
lowestInCircuit = j;
+
break;
+
}
+
}
+
circuitColors.set(root, junctionColors[lowestInCircuit]);
+
}
+
}
+
+
// Track which junctions are connected
+
const connectedJunctions = new Set();
+
stage.connections.forEach(conn => {
+
connectedJunctions.add(conn.i);
+
connectedJunctions.add(conn.j);
+
});
+
+
junctionMeshes.forEach((mesh, idx) => {
+
const root = uf.find(idx);
+
const color = circuitColors.get(root);
+
+
mesh.material = new THREE.MeshPhongMaterial({
+
color: color,
+
emissive: color,
+
emissiveIntensity: 0.4
+
});
+
+
// Make unconnected junctions smaller
+
if (connectedJunctions.has(idx)) {
+
mesh.scale.set(1, 1, 1);
+
} else {
+
mesh.scale.set(0.33, 0.33, 0.33);
+
}
+
});
+
+
// Show/hide cylinders and update their colors based on current stage
+
connectionCylinders.forEach((cylinder, idx) => {
+
if (idx < numConnections) {
+
cylinder.visible = true;
+
// Update color to match circuit
+
const root = uf.find(cylinder.userData.i);
+
const lineColor = circuitColors.get(root);
+
cylinder.material.color = lineColor;
+
} else {
+
cylinder.visible = false;
+
}
+
});
+
}
+
+
// Animation state
+
let currentStage = 0;
+
let isPlaying = false;
+
let lastTime = 0;
+
+
const playBtn = document.getElementById('play');
+
const prevBtn = document.getElementById('prev');
+
const nextBtn = document.getElementById('next');
+
const resetBtn = document.getElementById('reset');
+
const speedSlider = document.getElementById('speed');
+
const stepInfo = document.getElementById('stepInfo');
+
const statsInfo = document.getElementById('statsInfo');
+
+
function updateUI() {
+
const stage = stages[currentStage];
+
stepInfo.textContent = \`Step: \${currentStage} / 1000\`;
+
statsInfo.textContent = \`Circuits: \${stage.circuits} | Largest: \${stage.largest} | Product: \${stage.product.toLocaleString()}\`;
+
+
prevBtn.disabled = currentStage === 0;
+
nextBtn.disabled = currentStage === stages.length - 1;
+
+
updateConnections(stage);
+
}
+
+
function goToStage(index) {
+
currentStage = Math.max(0, Math.min(stages.length - 1, index));
+
updateUI();
+
}
+
+
prevBtn.addEventListener('click', () => goToStage(currentStage - 1));
+
nextBtn.addEventListener('click', () => goToStage(currentStage + 1));
+
resetBtn.addEventListener('click', () => goToStage(0));
+
+
playBtn.addEventListener('click', () => {
+
isPlaying = !isPlaying;
+
playBtn.textContent = isPlaying ? '⏸ Pause' : '▶ Play';
+
if (isPlaying && currentStage === stages.length - 1) {
+
goToStage(0);
+
}
+
});
+
+
document.addEventListener('keydown', (e) => {
+
if (e.key === 'ArrowLeft') prevBtn.click();
+
if (e.key === 'ArrowRight') nextBtn.click();
+
if (e.key === ' ') {
+
e.preventDefault();
+
playBtn.click();
+
}
+
if (e.key === 'r' || e.key === 'R') resetBtn.click();
+
});
+
+
window.addEventListener('resize', () => {
+
camera.aspect = window.innerWidth / window.innerHeight;
+
camera.updateProjectionMatrix();
+
renderer.setSize(window.innerWidth, window.innerHeight);
+
});
+
+
// Animation loop
+
function animate(time) {
+
requestAnimationFrame(animate);
+
+
// Auto-advance if playing
+
if (isPlaying) {
+
const speed = 1000 - parseInt(speedSlider.value);
+
if (time - lastTime > speed) {
+
if (currentStage < stages.length - 1) {
+
goToStage(currentStage + 1);
+
} else {
+
isPlaying = false;
+
playBtn.textContent = '▶ Play';
+
}
+
lastTime = time;
+
}
+
}
+
+
controls.update();
+
renderer.render(scene, camera);
+
}
+
+
// Initialize - start at step 0
+
currentStage = 0;
+
updateUI();
+
animate(0);
+
</script>
+
</body>
+
</html>`;
+
+
await Bun.write(`${scriptDir}/index.html`, html);
+
console.log(`Generated index.html with ${junctions.length} junction boxes`);
+592
vis/08/index.html
···
···
+
<!DOCTYPE html>
+
<html lang="en">
+
<head>
+
<meta charset="UTF-8">
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
<title>AoC 2025 Day 8 - Playground Junction Boxes</title>
+
<style>
+
* {
+
box-sizing: border-box;
+
margin: 0;
+
padding: 0;
+
}
+
body {
+
background: #1e1e2e;
+
color: #cdd6f4;
+
font-family: "Source Code Pro", monospace;
+
font-size: 14pt;
+
font-weight: 300;
+
overflow: hidden;
+
margin: 0;
+
padding: 0;
+
}
+
#container {
+
width: 100vw;
+
height: 100vh;
+
position: relative;
+
}
+
#canvas {
+
width: 100%;
+
height: 100%;
+
display: block;
+
position: absolute;
+
top: 0;
+
left: 0;
+
z-index: 0;
+
}
+
.ui {
+
position: absolute;
+
top: 0;
+
left: 0;
+
right: 0;
+
padding: 20px;
+
pointer-events: none;
+
z-index: 1;
+
}
+
.ui > * {
+
pointer-events: auto;
+
}
+
h1 {
+
color: #a6e3a1;
+
text-shadow: 0 0 2px #a6e3a1, 0 0 5px #a6e3a1;
+
margin-bottom: 10px;
+
font-size: 1em;
+
font-weight: normal;
+
text-align: center;
+
}
+
.controls {
+
background: rgba(17, 17, 27, 0.9);
+
border: 1px solid #313244;
+
padding: 15px;
+
margin: 15px auto;
+
max-width: 800px;
+
border-radius: 4px;
+
}
+
.control-row {
+
display: flex;
+
gap: 15px;
+
align-items: center;
+
margin-bottom: 10px;
+
flex-wrap: wrap;
+
justify-content: center;
+
}
+
.control-row:last-child {
+
margin-bottom: 0;
+
}
+
button {
+
background: #11111b;
+
color: #a6e3a1;
+
border: 1px solid #313244;
+
padding: 8px 16px;
+
cursor: pointer;
+
font-family: inherit;
+
font-size: 14px;
+
border-radius: 3px;
+
}
+
button:hover {
+
background: #181825;
+
}
+
button:disabled {
+
opacity: 0.5;
+
cursor: not-allowed;
+
}
+
.info {
+
color: #f9e2af;
+
font-size: 14px;
+
}
+
.stats {
+
background: rgba(17, 17, 27, 0.9);
+
border: 1px solid #313244;
+
padding: 10px 15px;
+
margin: 0 auto;
+
max-width: 800px;
+
border-radius: 4px;
+
text-align: center;
+
font-size: 13px;
+
color: #a6adc8;
+
}
+
.legend {
+
display: flex;
+
gap: 15px;
+
margin-top: 10px;
+
flex-wrap: wrap;
+
justify-content: center;
+
}
+
.legend-item {
+
display: flex;
+
align-items: center;
+
gap: 6px;
+
font-size: 12px;
+
color: #a6adc8;
+
}
+
.legend-box {
+
width: 12px;
+
height: 12px;
+
border-radius: 50%;
+
}
+
.legend-box.junction { background: #89b4fa; }
+
.legend-box.connection { background: #a6e3a1; }
+
.legend-box.circuit { background: #f9e2af; }
+
input[type="range"] {
+
-webkit-appearance: none;
+
appearance: none;
+
width: 120px;
+
height: 6px;
+
background: #313244;
+
outline: none;
+
border: 1px solid #313244;
+
}
+
input[type="range"]::-webkit-slider-thumb {
+
-webkit-appearance: none;
+
appearance: none;
+
width: 16px;
+
height: 16px;
+
background: #a6e3a1;
+
cursor: pointer;
+
border: 1px solid #313244;
+
border-radius: 50%;
+
}
+
input[type="range"]::-moz-range-thumb {
+
width: 16px;
+
height: 16px;
+
background: #a6e3a1;
+
cursor: pointer;
+
border: 1px solid #313244;
+
border-radius: 50%;
+
}
+
label {
+
color: #a6adc8;
+
font-size: 13px;
+
}
+
a {
+
text-decoration: none;
+
color: #a6e3a1;
+
outline: 0;
+
}
+
a:hover, a:focus {
+
text-decoration: underline;
+
}
+
</style>
+
</head>
+
<body>
+
<div id="container">
+
<canvas id="canvas"></canvas>
+
<div class="ui">
+
<h1>AoC 2025 Day 8 - Playground Junction Boxes</h1>
+
+
<div class="controls">
+
<div class="control-row">
+
<button id="prev">← Previous</button>
+
<button id="play">▶ Play</button>
+
<button id="next">Next →</button>
+
<button id="reset">↺ Reset</button>
+
</div>
+
<div class="control-row">
+
<label for="speed">Speed:</label>
+
<input type="range" id="speed" min="0" max="1000" value="900" step="5">
+
<span class="info" id="stepInfo">Step: 0 / 1000</span>
+
</div>
+
<div class="legend">
+
<div class="legend-item"><div class="legend-box junction"></div> Isolated Junction (small)</div>
+
<div class="legend-item"><div class="legend-box connection"></div> Connected Junction (large)</div>
+
<div class="legend-item"><div class="legend-box circuit"></div> Circuit (color-coded)</div>
+
</div>
+
</div>
+
+
<div class="stats">
+
<div id="statsInfo">Circuits: 1000 | Largest: 0 | Product: 0</div>
+
<div style="margin-top: 5px; font-size: 11px;"><a href="../index.html">[Return to Index]</a></div>
+
</div>
+
</div>
+
</div>
+
+
<script type="importmap">
+
{
+
"imports": {
+
"three": "https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js",
+
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/"
+
}
+
}
+
</script>
+
+
<script type="module">
+
import * as THREE from 'three';
+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
+
+
// Input data embedded
+
const junctions = [{"x":5161,"y":22203,"z":73169},{"x":66101,"y":34794,"z":41187},{"x":19740,"y":85581,"z":70637},{"x":64804,"y":51256,"z":2927},{"x":68224,"y":49993,"z":7867},{"x":9878,"y":97392,"z":46130},{"x":40001,"y":51521,"z":79544},{"x":45624,"y":73253,"z":74846},{"x":30230,"y":58923,"z":59531},{"x":84723,"y":58102,"z":86638},{"x":14537,"y":44841,"z":84567},{"x":41558,"y":92494,"z":89096},{"x":75012,"y":1225,"z":75685},{"x":77420,"y":67581,"z":11653},{"x":2365,"y":26154,"z":61974},{"x":80460,"y":35718,"z":17495},{"x":84603,"y":85352,"z":97745},{"x":85312,"y":81113,"z":25465},{"x":84808,"y":39686,"z":39315},{"x":78930,"y":20627,"z":23019},{"x":9138,"y":22120,"z":32917},{"x":43534,"y":38414,"z":62909},{"x":1852,"y":53657,"z":68936},{"x":42113,"y":82762,"z":91457},{"x":54027,"y":40542,"z":58053},{"x":82202,"y":42280,"z":72268},{"x":202,"y":67735,"z":80514},{"x":52305,"y":854,"z":45988},{"x":88343,"y":55523,"z":31987},{"x":28291,"y":46179,"z":65951},{"x":91364,"y":63556,"z":74551},{"x":86323,"y":53379,"z":5679},{"x":4978,"y":97691,"z":830},{"x":72221,"y":98591,"z":40848},{"x":76259,"y":31452,"z":38504},{"x":24278,"y":31745,"z":72816},{"x":43435,"y":38188,"z":12128},{"x":38931,"y":44301,"z":87982},{"x":12819,"y":90012,"z":60798},{"x":22440,"y":63786,"z":13427},{"x":72991,"y":66943,"z":5903},{"x":99694,"y":22765,"z":27658},{"x":14531,"y":64099,"z":3781},{"x":81830,"y":53305,"z":71039},{"x":16083,"y":37712,"z":94291},{"x":1861,"y":15536,"z":36482},{"x":49895,"y":20278,"z":79203},{"x":81296,"y":28469,"z":21142},{"x":18962,"y":44916,"z":45948},{"x":75556,"y":98208,"z":8719},{"x":17073,"y":99830,"z":64332},{"x":80061,"y":73938,"z":7885},{"x":84505,"y":81534,"z":49321},{"x":51314,"y":28742,"z":90563},{"x":45266,"y":91838,"z":54563},{"x":68720,"y":31965,"z":85325},{"x":17087,"y":71197,"z":81398},{"x":21515,"y":19915,"z":83771},{"x":98426,"y":60158,"z":81708},{"x":39695,"y":27980,"z":22169},{"x":74294,"y":16001,"z":70164},{"x":37834,"y":77125,"z":68205},{"x":50158,"y":66031,"z":33997},{"x":6228,"y":61768,"z":85916},{"x":18288,"y":41150,"z":22719},{"x":22002,"y":47425,"z":92208},{"x":56877,"y":4935,"z":3480},{"x":62885,"y":9582,"z":43220},{"x":40257,"y":2330,"z":33318},{"x":98759,"y":96290,"z":18372},{"x":73781,"y":23958,"z":3804},{"x":7471,"y":82734,"z":46760},{"x":35024,"y":97965,"z":71262},{"x":37867,"y":65856,"z":50384},{"x":52511,"y":81222,"z":52412},{"x":33482,"y":59673,"z":3263},{"x":5675,"y":59568,"z":35932},{"x":21251,"y":43750,"z":56819},{"x":96765,"y":7988,"z":87769},{"x":20307,"y":50290,"z":65243},{"x":17293,"y":84764,"z":12114},{"x":66302,"y":92742,"z":19412},{"x":72049,"y":98072,"z":97246},{"x":53960,"y":10572,"z":73353},{"x":17692,"y":2175,"z":43186},{"x":26181,"y":26692,"z":78570},{"x":6206,"y":68914,"z":3522},{"x":80114,"y":23493,"z":14095},{"x":35469,"y":79858,"z":91410},{"x":21464,"y":22046,"z":6372},{"x":39867,"y":59222,"z":96369},{"x":41647,"y":17493,"z":96062},{"x":21818,"y":91894,"z":75532},{"x":8309,"y":91873,"z":78553},{"x":211,"y":75084,"z":88402},{"x":70079,"y":49640,"z":88549},{"x":53250,"y":64935,"z":66594},{"x":59536,"y":22170,"z":30100},{"x":60861,"y":99105,"z":14003},{"x":61621,"y":27840,"z":90515},{"x":74639,"y":29893,"z":7322},{"x":84678,"y":40440,"z":94821},{"x":97113,"y":95314,"z":13072},{"x":68608,"y":49658,"z":91760},{"x":63585,"y":86976,"z":70868},{"x":80713,"y":2187,"z":45320},{"x":53966,"y":90337,"z":38684},{"x":12879,"y":57544,"z":67234},{"x":1808,"y":55865,"z":37081},{"x":29062,"y":1242,"z":56047},{"x":68536,"y":82413,"z":79663},{"x":82931,"y":37875,"z":80295},{"x":71837,"y":70100,"z":82856},{"x":8604,"y":29732,"z":15837},{"x":55168,"y":22932,"z":85866},{"x":7831,"y":59139,"z":11394},{"x":68246,"y":95928,"z":25366},{"x":5516,"y":88931,"z":66654},{"x":96479,"y":58400,"z":51809},{"x":21133,"y":61075,"z":29966},{"x":43770,"y":13343,"z":39028},{"x":75024,"y":25103,"z":58888},{"x":35728,"y":97042,"z":40407},{"x":93159,"y":78375,"z":73702},{"x":59258,"y":10570,"z":78625},{"x":58756,"y":87652,"z":98984},{"x":32438,"y":36234,"z":86197},{"x":42461,"y":39846,"z":29989},{"x":91102,"y":74517,"z":69917},{"x":24981,"y":69124,"z":48442},{"x":95106,"y":21901,"z":90494},{"x":52329,"y":5337,"z":48036},{"x":74828,"y":37780,"z":86916},{"x":73037,"y":37561,"z":82293},{"x":11261,"y":5900,"z":29591},{"x":47758,"y":40284,"z":70586},{"x":44437,"y":88862,"z":81499},{"x":2771,"y":47188,"z":65346},{"x":48737,"y":73833,"z":22256},{"x":6681,"y":31926,"z":14269},{"x":14890,"y":4721,"z":99568},{"x":74643,"y":59728,"z":83172},{"x":44018,"y":65177,"z":11311},{"x":28163,"y":95996,"z":55663},{"x":62284,"y":4057,"z":40282},{"x":85786,"y":8606,"z":16706},{"x":38301,"y":35943,"z":83825},{"x":93121,"y":30446,"z":83456},{"x":80709,"y":93878,"z":46043},{"x":25806,"y":35135,"z":61144},{"x":82648,"y":87205,"z":99228},{"x":81004,"y":39106,"z":48053},{"x":45717,"y":30505,"z":54773},{"x":97465,"y":49038,"z":15700},{"x":46560,"y":20959,"z":89092},{"x":41799,"y":67704,"z":577},{"x":36770,"y":96608,"z":79263},{"x":40028,"y":55549,"z":56718},{"x":14448,"y":88161,"z":97119},{"x":26854,"y":84620,"z":40111},{"x":70853,"y":7432,"z":76917},{"x":55200,"y":24264,"z":16287},{"x":59,"y":67218,"z":14635},{"x":6942,"y":65638,"z":37471},{"x":68640,"y":68354,"z":35571},{"x":40272,"y":87947,"z":27089},{"x":6692,"y":88929,"z":43004},{"x":21229,"y":45052,"z":37013},{"x":15926,"y":71370,"z":74945},{"x":92692,"y":75869,"z":34838},{"x":91181,"y":95437,"z":48530},{"x":18204,"y":12940,"z":45467},{"x":35323,"y":15520,"z":69239},{"x":82373,"y":40019,"z":86985},{"x":80756,"y":54592,"z":21710},{"x":48243,"y":31541,"z":44180},{"x":46122,"y":84026,"z":53779},{"x":39293,"y":93745,"z":62710},{"x":46031,"y":21084,"z":76780},{"x":18406,"y":22463,"z":33328},{"x":49421,"y":94086,"z":30128},{"x":76265,"y":81888,"z":38769},{"x":51061,"y":44872,"z":30360},{"x":11748,"y":24035,"z":63578},{"x":27664,"y":31317,"z":20723},{"x":41342,"y":56333,"z":80116},{"x":6524,"y":33672,"z":42019},{"x":36451,"y":16429,"z":91763},{"x":25211,"y":84537,"z":10563},{"x":57256,"y":68807,"z":87002},{"x":16992,"y":17366,"z":32886},{"x":2095,"y":66214,"z":26424},{"x":72798,"y":44489,"z":12613},{"x":72302,"y":77566,"z":22389},{"x":83839,"y":80165,"z":79968},{"x":11488,"y":5510,"z":666},{"x":13321,"y":26484,"z":91940},{"x":88598,"y":20942,"z":28033},{"x":45663,"y":93458,"z":8868},{"x":13632,"y":60855,"z":21950},{"x":69401,"y":17820,"z":15957},{"x":99693,"y":49351,"z":24797},{"x":76038,"y":24933,"z":31774},{"x":36063,"y":8828,"z":29548},{"x":91198,"y":49648,"z":72812},{"x":14950,"y":49734,"z":84990},{"x":29974,"y":24256,"z":43142},{"x":19381,"y":8237,"z":23391},{"x":22823,"y":5267,"z":23772},{"x":57408,"y":109,"z":81592},{"x":25135,"y":3341,"z":44930},{"x":22021,"y":39757,"z":75364},{"x":921,"y":25399,"z":58355},{"x":34100,"y":75425,"z":22714},{"x":49845,"y":80698,"z":15599},{"x":75078,"y":43967,"z":63211},{"x":33418,"y":70727,"z":81931},{"x":11894,"y":93560,"z":98772},{"x":18360,"y":40040,"z":11898},{"x":13341,"y":57104,"z":74958},{"x":20124,"y":85072,"z":36147},{"x":11466,"y":57461,"z":25681},{"x":4890,"y":38197,"z":35448},{"x":24857,"y":93228,"z":69880},{"x":50868,"y":43259,"z":83785},{"x":10058,"y":67418,"z":98282},{"x":14583,"y":66434,"z":53841},{"x":74869,"y":52825,"z":81807},{"x":17976,"y":7604,"z":55075},{"x":28345,"y":58341,"z":54910},{"x":23937,"y":31696,"z":20983},{"x":15299,"y":65989,"z":48101},{"x":85948,"y":89595,"z":74642},{"x":55386,"y":58983,"z":43994},{"x":99999,"y":44512,"z":37075},{"x":75074,"y":15909,"z":8571},{"x":86057,"y":69748,"z":62953},{"x":30817,"y":82342,"z":17104},{"x":90956,"y":22036,"z":54469},{"x":93486,"y":72096,"z":63425},{"x":21847,"y":61753,"z":27413},{"x":76878,"y":83913,"z":6736},{"x":47263,"y":86404,"z":11866},{"x":54473,"y":77380,"z":8133},{"x":43916,"y":77261,"z":44094},{"x":31411,"y":73190,"z":80946},{"x":57844,"y":38767,"z":42289},{"x":67832,"y":36459,"z":80545},{"x":50394,"y":7272,"z":47306},{"x":32819,"y":25545,"z":67025},{"x":69061,"y":50499,"z":87171},{"x":29321,"y":3573,"z":96393},{"x":790,"y":54881,"z":30567},{"x":10323,"y":19294,"z":61513},{"x":42390,"y":48286,"z":82826},{"x":48131,"y":42651,"z":2327},{"x":63796,"y":43817,"z":56908},{"x":28226,"y":55100,"z":69879},{"x":20961,"y":95836,"z":666},{"x":22399,"y":63924,"z":10001},{"x":8586,"y":66790,"z":40318},{"x":66927,"y":1619,"z":64174},{"x":34447,"y":39223,"z":88816},{"x":53211,"y":28305,"z":63761},{"x":59463,"y":98390,"z":3323},{"x":21005,"y":38029,"z":96138},{"x":18979,"y":86778,"z":11125},{"x":39421,"y":55489,"z":41546},{"x":66733,"y":67487,"z":21536},{"x":85741,"y":27543,"z":34114},{"x":54905,"y":19440,"z":27003},{"x":80920,"y":97553,"z":51524},{"x":64486,"y":94319,"z":16244},{"x":47427,"y":88423,"z":99263},{"x":94831,"y":59526,"z":29750},{"x":20612,"y":19198,"z":3428},{"x":36838,"y":45561,"z":55848},{"x":3739,"y":24234,"z":96805},{"x":30520,"y":80842,"z":25923},{"x":75905,"y":4527,"z":45717},{"x":57097,"y":36462,"z":29937},{"x":12449,"y":12092,"z":75382},{"x":16241,"y":91132,"z":18465},{"x":92421,"y":32601,"z":39642},{"x":94358,"y":19855,"z":92919},{"x":98029,"y":17123,"z":98277},{"x":62997,"y":9369,"z":62516},{"x":5743,"y":97599,"z":15596},{"x":55128,"y":96754,"z":63021},{"x":64749,"y":29348,"z":87579},{"x":50931,"y":49963,"z":16511},{"x":24104,"y":10431,"z":32239},{"x":85592,"y":78853,"z":55919},{"x":54462,"y":92986,"z":61923},{"x":82923,"y":98099,"z":69987},{"x":65451,"y":96563,"z":45754},{"x":56042,"y":38226,"z":27831},{"x":10024,"y":90940,"z":53047},{"x":51092,"y":54380,"z":11833},{"x":58088,"y":87728,"z":35943},{"x":71276,"y":19838,"z":71114},{"x":28525,"y":42958,"z":70906},{"x":88225,"y":61386,"z":12953},{"x":74256,"y":4303,"z":23200},{"x":49526,"y":36042,"z":15796},{"x":9153,"y":94788,"z":23665},{"x":80101,"y":23495,"z":50819},{"x":31569,"y":685,"z":20686},{"x":22964,"y":44468,"z":35471},{"x":70246,"y":98432,"z":61863},{"x":72794,"y":47183,"z":51421},{"x":6710,"y":30845,"z":79164},{"x":81930,"y":58931,"z":76876},{"x":60308,"y":77291,"z":46931},{"x":95213,"y":70380,"z":78860},{"x":39666,"y":17476,"z":7322},{"x":95063,"y":1905,"z":43670},{"x":54354,"y":12416,"z":76624},{"x":53740,"y":3491,"z":62481},{"x":12864,"y":30769,"z":52235},{"x":32045,"y":63460,"z":79567},{"x":75862,"y":90991,"z":84898},{"x":38669,"y":63989,"z":25022},{"x":7799,"y":5513,"z":46763},{"x":24068,"y":39447,"z":50344},{"x":36023,"y":279,"z":64133},{"x":5465,"y":72858,"z":7840},{"x":29160,"y":24991,"z":21515},{"x":59713,"y":27231,"z":3281},{"x":36164,"y":90203,"z":93847},{"x":75331,"y":71291,"z":86760},{"x":42565,"y":52454,"z":17080},{"x":6955,"y":23878,"z":44603},{"x":40854,"y":37169,"z":54460},{"x":5285,"y":66356,"z":26455},{"x":63058,"y":94851,"z":50037},{"x":12741,"y":4910,"z":77151},{"x":32824,"y":27982,"z":26924},{"x":47478,"y":85766,"z":35219},{"x":16457,"y":86168,"z":96792},{"x":83127,"y":34305,"z":62074},{"x":95548,"y":14535,"z":41035},{"x":88855,"y":5223,"z":94494},{"x":50146,"y":163,"z":79528},{"x":23338,"y":33348,"z":38377},{"x":4070,"y":21412,"z":15793},{"x":96775,"y":52388,"z":47999},{"x":12154,"y":63542,"z":15677},{"x":88504,"y":99262,"z":54679},{"x":12808,"y":85236,"z":5704},{"x":3906,"y":14610,"z":29712},{"x":32559,"y":83568,"z":27884},{"x":93026,"y":38658,"z":39179},{"x":14070,"y":67679,"z":15610},{"x":29713,"y":12625,"z":84667},{"x":6921,"y":19610,"z":86805},{"x":66462,"y":57191,"z":80287},{"x":60537,"y":75661,"z":4167},{"x":38067,"y":9388,"z":24090},{"x":92022,"y":81040,"z":60306},{"x":54484,"y":9192,"z":84268},{"x":36791,"y":90762,"z":60181},{"x":75947,"y":15733,"z":50393},{"x":16807,"y":86935,"z":85406},{"x":8328,"y":60108,"z":63149},{"x":71295,"y":14068,"z":96601},{"x":80613,"y":26940,"z":67177},{"x":89681,"y":62762,"z":34136},{"x":10217,"y":93743,"z":30456},{"x":73059,"y":67804,"z":71175},{"x":9005,"y":74733,"z":45169},{"x":43845,"y":10522,"z":61274},{"x":56005,"y":15428,"z":11649},{"x":83175,"y":17043,"z":59977},{"x":58911,"y":36157,"z":73078},{"x":18088,"y":79910,"z":55693},{"x":81267,"y":64100,"z":26580},{"x":56896,"y":25358,"z":96591},{"x":65687,"y":86996,"z":70012},{"x":77409,"y":95703,"z":52067},{"x":31023,"y":70310,"z":62432},{"x":47952,"y":90510,"z":54558},{"x":68213,"y":85589,"z":46380},{"x":58332,"y":90985,"z":35281},{"x":54333,"y":45220,"z":15414},{"x":94029,"y":8669,"z":19406},{"x":14313,"y":90201,"z":57318},{"x":93133,"y":35598,"z":4739},{"x":57923,"y":86809,"z":49393},{"x":96319,"y":38176,"z":37542},{"x":88108,"y":46715,"z":16554},{"x":57259,"y":86355,"z":83582},{"x":92724,"y":64865,"z":59543},{"x":21703,"y":12027,"z":77429},{"x":82537,"y":84727,"z":10231},{"x":69466,"y":70604,"z":11687},{"x":25405,"y":34281,"z":72216},{"x":13429,"y":28547,"z":21532},{"x":50561,"y":60774,"z":36501},{"x":96078,"y":21610,"z":59197},{"x":75502,"y":7393,"z":90387},{"x":84406,"y":14971,"z":69869},{"x":63056,"y":23127,"z":75447},{"x":2987,"y":35313,"z":26884},{"x":75976,"y":38124,"z":62378},{"x":50678,"y":51910,"z":64734},{"x":92897,"y":9414,"z":78851},{"x":12385,"y":9019,"z":52962},{"x":49969,"y":73736,"z":13654},{"x":85171,"y":97718,"z":94935},{"x":69860,"y":46369,"z":35285},{"x":48996,"y":47871,"z":45331},{"x":34138,"y":70912,"z":14473},{"x":81860,"y":631,"z":53276},{"x":94452,"y":580,"z":73549},{"x":62698,"y":2127,"z":79924},{"x":25326,"y":51241,"z":35453},{"x":96322,"y":9704,"z":66070},{"x":18980,"y":34897,"z":88148},{"x":43690,"y":93420,"z":6598},{"x":74205,"y":53286,"z":67674},{"x":3254,"y":30109,"z":93966},{"x":57779,"y":91666,"z":11952},{"x":72134,"y":40940,"z":44488},{"x":99873,"y":83052,"z":1126},{"x":97309,"y":37450,"z":91956},{"x":55829,"y":40306,"z":91718},{"x":78390,"y":46731,"z":20057},{"x":91883,"y":47744,"z":85866},{"x":78125,"y":3512,"z":78114},{"x":54410,"y":61872,"z":96284},{"x":11630,"y":4634,"z":48245},{"x":583,"y":97186,"z":63568},{"x":50298,"y":62275,"z":77450},{"x":46247,"y":70570,"z":70214},{"x":94826,"y":69412,"z":5578},{"x":78568,"y":78523,"z":95664},{"x":36379,"y":91084,"z":3661},{"x":11137,"y":95017,"z":38825},{"x":71379,"y":44935,"z":23389},{"x":22116,"y":10182,"z":19393},{"x":26631,"y":14386,"z":8913},{"x":90244,"y":18267,"z":36040},{"x":53393,"y":4365,"z":99048},{"x":4079,"y":95272,"z":99036},{"x":59709,"y":84380,"z":53915},{"x":65569,"y":42790,"z":12489},{"x":61765,"y":46485,"z":97361},{"x":82601,"y":59377,"z":69738},{"x":59108,"y":17968,"z":28167},{"x":6237,"y":47107,"z":46872},{"x":93572,"y":74166,"z":87007},{"x":92142,"y":17034,"z":81637},{"x":11605,"y":97185,"z":26640},{"x":69469,"y":6432,"z":23789},{"x":69985,"y":80520,"z":26503},{"x":65480,"y":41448,"z":77875},{"x":16205,"y":81692,"z":74363},{"x":69176,"y":87344,"z":11992},{"x":57385,"y":71058,"z":8987},{"x":23453,"y":46306,"z":89249},{"x":9957,"y":58219,"z":86932},{"x":64935,"y":2339,"z":20205},{"x":31980,"y":85097,"z":77988},{"x":90690,"y":42666,"z":7944},{"x":74914,"y":20830,"z":82559},{"x":68530,"y":35265,"z":76156},{"x":82762,"y":4040,"z":99880},{"x":23472,"y":30526,"z":42857},{"x":87759,"y":55861,"z":81598},{"x":1957,"y":63841,"z":57021},{"x":44944,"y":18845,"z":90230},{"x":54251,"y":38708,"z":38260},{"x":8870,"y":753,"z":71611},{"x":81423,"y":5959,"z":45753},{"x":94142,"y":43274,"z":37696},{"x":86379,"y":70304,"z":7891},{"x":44515,"y":72191,"z":1194},{"x":6205,"y":77653,"z":33542},{"x":92967,"y":18453,"z":48577},{"x":7678,"y":67117,"z":32246},{"x":32575,"y":51952,"z":80296},{"x":22384,"y":73600,"z":14490},{"x":52711,"y":30082,"z":25106},{"x":57480,"y":42599,"z":8822},{"x":43114,"y":59009,"z":65318},{"x":15762,"y":17130,"z":11220},{"x":70564,"y":47248,"z":83517},{"x":2121,"y":75358,"z":38760},{"x":35749,"y":33046,"z":52462},{"x":75238,"y":37380,"z":60124},{"x":96192,"y":97611,"z":49292},{"x":97758,"y":97953,"z":72104},{"x":58423,"y":34477,"z":87667},{"x":38902,"y":67350,"z":99834},{"x":42943,"y":30562,"z":94103},{"x":87924,"y":51390,"z":54745},{"x":81506,"y":4822,"z":81958},{"x":53178,"y":12282,"z":19647},{"x":85139,"y":63746,"z":27671},{"x":81487,"y":89072,"z":36336},{"x":6253,"y":25748,"z":86215},{"x":48027,"y":49098,"z":56556},{"x":43576,"y":68684,"z":55894},{"x":18254,"y":6493,"z":84642},{"x":52909,"y":84771,"z":72402},{"x":65560,"y":53513,"z":82375},{"x":81009,"y":57728,"z":62936},{"x":3688,"y":11379,"z":22586},{"x":26035,"y":72184,"z":18309},{"x":35242,"y":7329,"z":55693},{"x":71894,"y":92384,"z":38569},{"x":14615,"y":55929,"z":26218},{"x":26019,"y":15150,"z":17171},{"x":66669,"y":33277,"z":40758},{"x":24931,"y":4862,"z":27660},{"x":75483,"y":86544,"z":67579},{"x":5083,"y":12873,"z":65276},{"x":1313,"y":87524,"z":29121},{"x":286,"y":17310,"z":28052},{"x":31431,"y":78066,"z":80220},{"x":97016,"y":30080,"z":85033},{"x":55036,"y":47034,"z":83591},{"x":29322,"y":15017,"z":27875},{"x":74140,"y":43294,"z":70313},{"x":11167,"y":98589,"z":17524},{"x":98795,"y":81059,"z":18560},{"x":12125,"y":50747,"z":94693},{"x":29937,"y":96225,"z":94110},{"x":35740,"y":63048,"z":74201},{"x":60963,"y":97471,"z":54331},{"x":25085,"y":26113,"z":87315},{"x":79011,"y":55630,"z":79771},{"x":42718,"y":62834,"z":7988},{"x":66051,"y":65078,"z":5058},{"x":69214,"y":22915,"z":92689},{"x":49908,"y":23340,"z":39297},{"x":69887,"y":84399,"z":92649},{"x":78657,"y":85796,"z":70300},{"x":93483,"y":74898,"z":95070},{"x":46907,"y":50571,"z":41782},{"x":70877,"y":41047,"z":48729},{"x":41524,"y":75771,"z":99337},{"x":53466,"y":47701,"z":38633},{"x":12762,"y":44499,"z":22350},{"x":44688,"y":9488,"z":25293},{"x":71430,"y":21562,"z":52514},{"x":66810,"y":36914,"z":96406},{"x":99100,"y":39368,"z":50027},{"x":3160,"y":44565,"z":15249},{"x":63424,"y":89275,"z":68274},{"x":97623,"y":53617,"z":59825},{"x":2322,"y":2086,"z":37512},{"x":40230,"y":51063,"z":69967},{"x":13255,"y":99386,"z":61085},{"x":76434,"y":51331,"z":71427},{"x":20341,"y":71310,"z":18982},{"x":43964,"y":43210,"z":37969},{"x":36453,"y":34957,"z":99037},{"x":33461,"y":99489,"z":89885},{"x":47027,"y":40641,"z":91396},{"x":90242,"y":14775,"z":6617},{"x":77782,"y":57333,"z":73540},{"x":98828,"y":75818,"z":59586},{"x":25833,"y":94734,"z":11049},{"x":23932,"y":92894,"z":49069},{"x":82190,"y":13166,"z":17162},{"x":96342,"y":7060,"z":51505},{"x":31207,"y":39374,"z":141},{"x":63816,"y":76588,"z":17759},{"x":59091,"y":83198,"z":27627},{"x":15102,"y":8698,"z":84051},{"x":13417,"y":49225,"z":56704},{"x":9458,"y":71311,"z":45454},{"x":46120,"y":10697,"z":75585},{"x":78524,"y":87896,"z":27662},{"x":99544,"y":44501,"z":37210},{"x":77089,"y":31087,"z":11164},{"x":19136,"y":55656,"z":28658},{"x":96836,"y":89792,"z":77233},{"x":1573,"y":69768,"z":70774},{"x":34548,"y":4159,"z":64615},{"x":97621,"y":88974,"z":97672},{"x":16412,"y":95340,"z":19485},{"x":6842,"y":70744,"z":21696},{"x":27216,"y":62222,"z":95880},{"x":54156,"y":26578,"z":79466},{"x":40425,"y":6222,"z":41375},{"x":96958,"y":27155,"z":62281},{"x":69478,"y":3232,"z":61554},{"x":12529,"y":31863,"z":2982},{"x":31439,"y":47831,"z":1305},{"x":27766,"y":33929,"z":62205},{"x":28107,"y":15467,"z":47852},{"x":82990,"y":80468,"z":96872},{"x":66036,"y":57413,"z":76555},{"x":12420,"y":63388,"z":22307},{"x":55157,"y":75636,"z":36508},{"x":51115,"y":58706,"z":29691},{"x":49857,"y":40192,"z":53190},{"x":51028,"y":36248,"z":72849},{"x":96395,"y":20052,"z":57260},{"x":94013,"y":71425,"z":47753},{"x":46776,"y":76728,"z":4429},{"x":52826,"y":60416,"z":51781},{"x":82089,"y":73840,"z":90552},{"x":67515,"y":19072,"z":62369},{"x":93647,"y":14133,"z":12132},{"x":86372,"y":9775,"z":77886},{"x":65032,"y":18651,"z":36941},{"x":29189,"y":6126,"z":46728},{"x":91647,"y":15409,"z":23234},{"x":11242,"y":36242,"z":45807},{"x":53248,"y":16864,"z":60107},{"x":71327,"y":27046,"z":9222},{"x":50402,"y":75876,"z":44510},{"x":33404,"y":96699,"z":84855},{"x":45579,"y":83811,"z":17786},{"x":39497,"y":27697,"z":84681},{"x":36596,"y":86508,"z":9225},{"x":58008,"y":26663,"z":48254},{"x":44342,"y":7399,"z":9660},{"x":1899,"y":94854,"z":78032},{"x":55241,"y":41264,"z":65612},{"x":14171,"y":61023,"z":83439},{"x":15896,"y":85118,"z":19571},{"x":26262,"y":92097,"z":60999},{"x":90827,"y":51591,"z":55977},{"x":2986,"y":30523,"z":93726},{"x":46290,"y":64597,"z":98545},{"x":69550,"y":43907,"z":46248},{"x":14666,"y":65895,"z":65914},{"x":83466,"y":3766,"z":45688},{"x":92475,"y":98128,"z":98120},{"x":14468,"y":1300,"z":83791},{"x":42359,"y":56603,"z":13864},{"x":1998,"y":87292,"z":30935},{"x":30636,"y":21447,"z":4608},{"x":10281,"y":80457,"z":60591},{"x":5927,"y":61652,"z":33198},{"x":84792,"y":90194,"z":23668},{"x":21375,"y":39200,"z":63981},{"x":26148,"y":2563,"z":59453},{"x":3878,"y":71863,"z":32303},{"x":24688,"y":40615,"z":21628},{"x":39305,"y":95887,"z":5211},{"x":77164,"y":28994,"z":894},{"x":60900,"y":79225,"z":35865},{"x":83313,"y":55862,"z":45365},{"x":93289,"y":62769,"z":33883},{"x":78270,"y":54992,"z":59444},{"x":78875,"y":24007,"z":44581},{"x":6586,"y":98225,"z":43721},{"x":7609,"y":14475,"z":55643},{"x":52655,"y":19354,"z":1554},{"x":90896,"y":48830,"z":55935},{"x":79908,"y":38414,"z":98854},{"x":56763,"y":69447,"z":14243},{"x":20261,"y":3353,"z":28051},{"x":48847,"y":25039,"z":69183},{"x":73095,"y":27262,"z":96065},{"x":83789,"y":46377,"z":66387},{"x":28015,"y":18283,"z":30259},{"x":92510,"y":84207,"z":68258},{"x":68858,"y":44507,"z":25847},{"x":51411,"y":84735,"z":80122},{"x":24991,"y":5280,"z":32444},{"x":47479,"y":24448,"z":5575},{"x":9688,"y":93891,"z":63995},{"x":93516,"y":32229,"z":95595},{"x":49792,"y":8956,"z":75349},{"x":82466,"y":2659,"z":42943},{"x":84831,"y":76883,"z":60572},{"x":17885,"y":26212,"z":32434},{"x":72426,"y":43039,"z":45242},{"x":38540,"y":73027,"z":74988},{"x":75250,"y":83395,"z":25528},{"x":60282,"y":63543,"z":61072},{"x":16881,"y":25850,"z":35010},{"x":4519,"y":87097,"z":63686},{"x":47418,"y":98765,"z":14632},{"x":82891,"y":70986,"z":16303},{"x":1366,"y":54258,"z":91108},{"x":46829,"y":42539,"z":81531},{"x":54600,"y":7812,"z":3535},{"x":7137,"y":68680,"z":1136},{"x":88358,"y":8124,"z":599},{"x":69247,"y":88524,"z":96202},{"x":92311,"y":35169,"z":68307},{"x":91805,"y":96856,"z":32492},{"x":27952,"y":60153,"z":4739},{"x":30620,"y":3652,"z":43088},{"x":73324,"y":88130,"z":7501},{"x":86253,"y":9120,"z":62975},{"x":85991,"y":90883,"z":11260},{"x":26466,"y":18263,"z":33451},{"x":39237,"y":61569,"z":79497},{"x":37689,"y":90031,"z":38342},{"x":34696,"y":18264,"z":9226},{"x":45350,"y":47447,"z":78459},{"x":62677,"y":45853,"z":74744},{"x":57411,"y":67900,"z":45868},{"x":7048,"y":66892,"z":23560},{"x":87734,"y":35737,"z":27665},{"x":61109,"y":48318,"z":39804},{"x":36411,"y":27,"z":28578},{"x":6227,"y":24921,"z":9488},{"x":53477,"y":26195,"z":86182},{"x":77449,"y":71240,"z":84188},{"x":38630,"y":98127,"z":69702},{"x":99055,"y":60171,"z":99999},{"x":83349,"y":97372,"z":85844},{"x":39341,"y":86407,"z":75472},{"x":47680,"y":96358,"z":89076},{"x":2527,"y":71367,"z":872},{"x":30127,"y":11915,"z":92875},{"x":27824,"y":89266,"z":39931},{"x":38712,"y":86498,"z":96990},{"x":81776,"y":27828,"z":54759},{"x":63372,"y":15940,"z":11399},{"x":6071,"y":66057,"z":84756},{"x":11294,"y":44057,"z":62574},{"x":35475,"y":49648,"z":234},{"x":24252,"y":10872,"z":63749},{"x":63614,"y":91455,"z":75505},{"x":98426,"y":38129,"z":43879},{"x":31629,"y":13292,"z":31049},{"x":17440,"y":64268,"z":44872},{"x":55257,"y":18709,"z":72695},{"x":96667,"y":64655,"z":65603},{"x":16652,"y":52245,"z":63140},{"x":54593,"y":16042,"z":75859},{"x":62373,"y":68010,"z":91287},{"x":14761,"y":50102,"z":47525},{"x":48995,"y":93258,"z":52452},{"x":35039,"y":36326,"z":85135},{"x":53151,"y":19858,"z":18208},{"x":66066,"y":14395,"z":2972},{"x":69065,"y":88336,"z":55024},{"x":94201,"y":5660,"z":82690},{"x":16758,"y":45854,"z":30312},{"x":86733,"y":56045,"z":24153},{"x":60422,"y":75296,"z":63216},{"x":55152,"y":16869,"z":38967},{"x":40203,"y":10071,"z":52315},{"x":24979,"y":79489,"z":22106},{"x":62418,"y":36395,"z":28813},{"x":36002,"y":25787,"z":36541},{"x":5398,"y":67134,"z":19608},{"x":45567,"y":29666,"z":99903},{"x":10198,"y":53619,"z":78720},{"x":48266,"y":94275,"z":32975},{"x":44723,"y":73473,"z":86109},{"x":71654,"y":10515,"z":46265},{"x":96533,"y":99156,"z":95256},{"x":20173,"y":16315,"z":46171},{"x":55137,"y":67991,"z":2808},{"x":3277,"y":59718,"z":20458},{"x":24998,"y":86919,"z":72481},{"x":31496,"y":67973,"z":16417},{"x":96712,"y":1737,"z":48781},{"x":74575,"y":64801,"z":96462},{"x":87339,"y":2057,"z":43083},{"x":47263,"y":89361,"z":36045},{"x":45953,"y":81799,"z":84544},{"x":85626,"y":35456,"z":62954},{"x":69021,"y":66867,"z":67040},{"x":68578,"y":93277,"z":41488},{"x":2494,"y":14008,"z":27502},{"x":94737,"y":79155,"z":20947},{"x":8300,"y":95363,"z":14661},{"x":4261,"y":1566,"z":36906},{"x":61289,"y":70259,"z":4340},{"x":12951,"y":58498,"z":60371},{"x":97025,"y":16001,"z":79457},{"x":98406,"y":60266,"z":21369},{"x":23175,"y":202,"z":97114},{"x":63765,"y":71722,"z":25127},{"x":80262,"y":89066,"z":48532},{"x":10178,"y":50750,"z":60284},{"x":73012,"y":51122,"z":73821},{"x":71962,"y":85888,"z":29954},{"x":49604,"y":27436,"z":3498},{"x":33824,"y":41959,"z":18841},{"x":65525,"y":44351,"z":33838},{"x":19647,"y":44807,"z":75944},{"x":18365,"y":17396,"z":48109},{"x":12342,"y":62941,"z":32471},{"x":19433,"y":88248,"z":31068},{"x":53830,"y":61194,"z":20751},{"x":50327,"y":80044,"z":70438},{"x":47479,"y":82735,"z":53545},{"x":86533,"y":36374,"z":88149},{"x":76215,"y":10963,"z":7959},{"x":95856,"y":68203,"z":69083},{"x":44406,"y":82301,"z":90909},{"x":75945,"y":41115,"z":32836},{"x":75323,"y":64386,"z":3301},{"x":76595,"y":43267,"z":62186},{"x":71018,"y":80419,"z":60598},{"x":36152,"y":87027,"z":46004},{"x":82895,"y":60201,"z":73317},{"x":21221,"y":20024,"z":32799},{"x":30839,"y":36544,"z":19137},{"x":50665,"y":7076,"z":16737},{"x":85144,"y":25721,"z":53984},{"x":77746,"y":44861,"z":87239},{"x":29612,"y":18252,"z":73085},{"x":63340,"y":8179,"z":74011},{"x":94270,"y":58535,"z":51866},{"x":63687,"y":25565,"z":45329},{"x":42012,"y":17325,"z":65275},{"x":79074,"y":21032,"z":39506},{"x":97103,"y":92135,"z":77620},{"x":42475,"y":76269,"z":40353},{"x":79254,"y":74928,"z":93712},{"x":24498,"y":82815,"z":95878},{"x":24267,"y":44573,"z":4849},{"x":80452,"y":22788,"z":84351},{"x":86115,"y":97837,"z":17072},{"x":6138,"y":63056,"z":38463},{"x":21903,"y":56589,"z":68556},{"x":14741,"y":63246,"z":33703},{"x":22351,"y":52367,"z":5528},{"x":53539,"y":47613,"z":94853},{"x":44785,"y":38786,"z":14171},{"x":88087,"y":94500,"z":56183},{"x":73700,"y":2726,"z":3842},{"x":16250,"y":73010,"z":1209},{"x":55705,"y":14057,"z":73268},{"x":59932,"y":40685,"z":24530},{"x":92494,"y":20475,"z":37609},{"x":91863,"y":64924,"z":42834},{"x":46489,"y":61374,"z":49425},{"x":94363,"y":39872,"z":55202},{"x":13410,"y":98087,"z":45172},{"x":22051,"y":11324,"z":45962},{"x":44849,"y":25662,"z":88491},{"x":75769,"y":63796,"z":1104},{"x":65563,"y":73886,"z":19426},{"x":71046,"y":47690,"z":95322},{"x":74551,"y":83083,"z":69651},{"x":8615,"y":4078,"z":32306},{"x":77226,"y":21601,"z":16840},{"x":92787,"y":80265,"z":48396},{"x":65273,"y":45058,"z":4977},{"x":42226,"y":7234,"z":80530},{"x":32766,"y":81532,"z":34163},{"x":83798,"y":3241,"z":84369},{"x":38469,"y":67001,"z":76815},{"x":1894,"y":88267,"z":7920},{"x":45806,"y":45132,"z":89732},{"x":30945,"y":91180,"z":87272},{"x":1006,"y":55022,"z":65523},{"x":46291,"y":77146,"z":7658},{"x":60054,"y":55561,"z":86698},{"x":19915,"y":80670,"z":65432},{"x":56270,"y":14003,"z":18444},{"x":30490,"y":99328,"z":26538},{"x":37477,"y":78725,"z":5663},{"x":85532,"y":9745,"z":63388},{"x":57982,"y":50202,"z":93462},{"x":15320,"y":29847,"z":5357},{"x":49269,"y":86040,"z":48487},{"x":57114,"y":68294,"z":82371},{"x":95011,"y":61493,"z":60676},{"x":32850,"y":78313,"z":74952},{"x":12425,"y":24856,"z":59986},{"x":61739,"y":94986,"z":51353},{"x":25916,"y":38678,"z":93921},{"x":14719,"y":91750,"z":8822},{"x":70751,"y":25157,"z":31902},{"x":17034,"y":9440,"z":41006},{"x":18664,"y":82908,"z":58921},{"x":36354,"y":22416,"z":73832},{"x":64615,"y":82081,"z":20657},{"x":2180,"y":53837,"z":43929},{"x":71746,"y":26827,"z":47862},{"x":35313,"y":46818,"z":29867},{"x":63995,"y":83312,"z":62542},{"x":26389,"y":18027,"z":38909},{"x":89290,"y":58554,"z":65877},{"x":72755,"y":9306,"z":67463},{"x":34796,"y":76540,"z":91702},{"x":28031,"y":92356,"z":38544},{"x":24223,"y":56569,"z":15528},{"x":76978,"y":72802,"z":49103},{"x":33532,"y":12892,"z":20891},{"x":4541,"y":85088,"z":8174},{"x":1527,"y":12502,"z":12337},{"x":15927,"y":71127,"z":5248},{"x":96299,"y":18029,"z":68425},{"x":52808,"y":90723,"z":37783},{"x":92606,"y":60196,"z":77969},{"x":55899,"y":73686,"z":52560},{"x":23919,"y":13265,"z":86545},{"x":58996,"y":69280,"z":19651},{"x":51013,"y":16113,"z":1727},{"x":71789,"y":30209,"z":56287},{"x":71296,"y":92980,"z":91882},{"x":33408,"y":92160,"z":76319},{"x":42048,"y":10919,"z":83857},{"x":28792,"y":74297,"z":86478},{"x":5389,"y":75937,"z":81049},{"x":79159,"y":44718,"z":95910},{"x":77812,"y":6109,"z":6903},{"x":11429,"y":2081,"z":46678},{"x":28728,"y":57702,"z":40446},{"x":85490,"y":69973,"z":11074},{"x":18985,"y":5870,"z":9670},{"x":41900,"y":64465,"z":86223},{"x":64031,"y":20749,"z":69030},{"x":19968,"y":63958,"z":89442},{"x":7769,"y":52832,"z":45916},{"x":95865,"y":7808,"z":98612},{"x":39867,"y":62263,"z":12449},{"x":83497,"y":47866,"z":75410},{"x":82557,"y":53917,"z":16242},{"x":9807,"y":26078,"z":60255},{"x":15042,"y":22029,"z":69166},{"x":30024,"y":51812,"z":46565},{"x":92338,"y":60566,"z":9364},{"x":57715,"y":22310,"z":11197},{"x":97140,"y":46213,"z":37849},{"x":74647,"y":55761,"z":78801},{"x":6299,"y":96259,"z":38989},{"x":40357,"y":37111,"z":41316},{"x":30968,"y":92364,"z":46696},{"x":76414,"y":51273,"z":99009},{"x":7118,"y":39225,"z":38640},{"x":17737,"y":14009,"z":58457},{"x":39944,"y":3341,"z":30639},{"x":84410,"y":19567,"z":41648},{"x":15931,"y":44298,"z":65121},{"x":79307,"y":94793,"z":80987},{"x":952,"y":58766,"z":5923},{"x":69268,"y":84830,"z":19869},{"x":51994,"y":80483,"z":45526},{"x":32711,"y":76202,"z":36436},{"x":71893,"y":59114,"z":79883},{"x":99066,"y":65336,"z":68361},{"x":41506,"y":31715,"z":45108},{"x":82661,"y":73919,"z":50362},{"x":86802,"y":61113,"z":87896},{"x":80840,"y":25948,"z":71017},{"x":37009,"y":56980,"z":26364},{"x":75120,"y":7694,"z":92969},{"x":64429,"y":15686,"z":26000},{"x":46498,"y":72376,"z":35606},{"x":86689,"y":27004,"z":55646},{"x":4210,"y":73749,"z":54409},{"x":98698,"y":23473,"z":57220},{"x":10744,"y":80544,"z":68264},{"x":87968,"y":61897,"z":73680},{"x":52729,"y":8385,"z":35266},{"x":18864,"y":31204,"z":87105},{"x":86199,"y":81893,"z":5053},{"x":24062,"y":50963,"z":68619},{"x":61890,"y":58242,"z":84398},{"x":20147,"y":67830,"z":14430},{"x":98776,"y":33533,"z":40129},{"x":12323,"y":27694,"z":64221},{"x":72068,"y":17313,"z":20441},{"x":7514,"y":65746,"z":67963},{"x":52487,"y":28248,"z":54826},{"x":71958,"y":4904,"z":93581},{"x":98092,"y":16710,"z":97076},{"x":54744,"y":77145,"z":9758},{"x":44623,"y":43540,"z":95666},{"x":9774,"y":88326,"z":80075},{"x":12941,"y":63893,"z":95040},{"x":94286,"y":36626,"z":59162},{"x":51358,"y":17484,"z":86901},{"x":40056,"y":64515,"z":77119},{"x":87783,"y":80188,"z":43564},{"x":98201,"y":84610,"z":75208},{"x":61098,"y":97658,"z":6479},{"x":86959,"y":5239,"z":38368},{"x":73286,"y":38785,"z":89091},{"x":23913,"y":59499,"z":26416},{"x":25602,"y":57654,"z":72854},{"x":63260,"y":85932,"z":86084},{"x":13537,"y":48315,"z":73193},{"x":19335,"y":11368,"z":43913},{"x":83423,"y":60937,"z":37261},{"x":9313,"y":47305,"z":36979},{"x":723,"y":518,"z":67639},{"x":46040,"y":85305,"z":22039},{"x":31475,"y":33436,"z":47686},{"x":31640,"y":79385,"z":76978},{"x":27354,"y":39004,"z":31931},{"x":62329,"y":20080,"z":37076},{"x":81285,"y":85346,"z":48141},{"x":12914,"y":98357,"z":15867},{"x":97984,"y":34251,"z":11049},{"x":12225,"y":26767,"z":39580},{"x":88412,"y":31758,"z":79197},{"x":14614,"y":36309,"z":35051},{"x":84857,"y":60361,"z":669},{"x":51578,"y":50383,"z":82234}];
+
+
// Normalize coordinates to fit in view
+
const maxCoord = Math.max(...junctions.flatMap(j => [j.x, j.y, j.z]));
+
const scale = 20 / maxCoord;
+
+
// Calculate all pairwise distances
+
function distance(a, b) {
+
return Math.sqrt(
+
(a.x - b.x) ** 2 +
+
(a.y - b.y) ** 2 +
+
(a.z - b.z) ** 2
+
);
+
}
+
+
const pairs = [];
+
for (let i = 0; i < junctions.length; i++) {
+
for (let j = i + 1; j < junctions.length; j++) {
+
pairs.push({
+
i,
+
j,
+
distance: distance(junctions[i], junctions[j])
+
});
+
}
+
}
+
+
// Sort by distance
+
pairs.sort((a, b) => a.distance - b.distance);
+
+
// Union-Find for circuit tracking
+
class UnionFind {
+
constructor(n) {
+
this.parent = Array.from({ length: n }, (_, i) => i);
+
this.size = Array(n).fill(1);
+
}
+
+
find(x) {
+
if (this.parent[x] !== x) {
+
this.parent[x] = this.find(this.parent[x]);
+
}
+
return this.parent[x];
+
}
+
+
union(x, y) {
+
const rootX = this.find(x);
+
const rootY = this.find(y);
+
+
if (rootX === rootY) return false;
+
+
if (this.size[rootX] < this.size[rootY]) {
+
this.parent[rootX] = rootY;
+
this.size[rootY] += this.size[rootX];
+
} else {
+
this.parent[rootY] = rootX;
+
this.size[rootX] += this.size[rootY];
+
}
+
+
return true;
+
}
+
+
getCircuitSizes() {
+
const circuits = new Map();
+
for (let i = 0; i < this.parent.length; i++) {
+
const root = this.find(i);
+
circuits.set(root, this.size[root]);
+
}
+
return Array.from(circuits.values()).sort((a, b) => b - a);
+
}
+
}
+
+
// Build animation stages (connections to make)
+
const stages = [];
+
const uf = new UnionFind(junctions.length);
+
const connections = [];
+
+
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;
+
+
stages.push({
+
connections: [...connections],
+
circuits: circuitSizes.length,
+
largest: circuitSizes[0] || 1,
+
product,
+
circuitSizes: [...circuitSizes]
+
});
+
+
if (step < 1000) {
+
const pair = pairs[step];
+
uf.union(pair.i, pair.j);
+
connections.push(pair);
+
}
+
}
+
+
// Three.js setup
+
const scene = new THREE.Scene();
+
scene.background = new THREE.Color(0x0d0d15);
+
+
const camera = new THREE.PerspectiveCamera(
+
75,
+
window.innerWidth / window.innerHeight,
+
0.1,
+
1000
+
);
+
camera.position.set(15, 15, 15);
+
+
const renderer = new THREE.WebGLRenderer({
+
canvas: document.getElementById('canvas'),
+
antialias: true
+
});
+
renderer.setSize(window.innerWidth, window.innerHeight);
+
renderer.setPixelRatio(window.devicePixelRatio);
+
+
const controls = new OrbitControls(camera, renderer.domElement);
+
controls.enableDamping = true;
+
controls.dampingFactor = 0.05;
+
controls.target.set(0, 3, 0); // Shift the view down by 3 units
+
controls.autoRotate = true;
+
controls.autoRotateSpeed = 0.5; // Slow rotation (0.5 degrees per frame at 60fps = 30 seconds per rotation)
+
controls.update();
+
+
// Lighting
+
const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
+
scene.add(ambientLight);
+
+
const pointLight1 = new THREE.PointLight(0xffffff, 0.6);
+
pointLight1.position.set(10, 10, 10);
+
scene.add(pointLight1);
+
+
const pointLight2 = new THREE.PointLight(0xffffff, 0.4);
+
pointLight2.position.set(-10, -10, -10);
+
scene.add(pointLight2);
+
+
// Create junction boxes (smaller)
+
const junctionGeometry = new THREE.SphereGeometry(0.1, 12, 12);
+
const junctionMaterial = new THREE.MeshPhongMaterial({
+
color: 0x89b4fa,
+
emissive: 0x89b4fa,
+
emissiveIntensity: 0.2
+
});
+
+
const junctionMeshes = junctions.map((j, idx) => {
+
const mesh = new THREE.Mesh(junctionGeometry, junctionMaterial);
+
mesh.position.set(
+
(j.x * scale) - 10,
+
(j.y * scale) - 10,
+
(j.z * scale) - 10
+
);
+
mesh.userData.index = idx;
+
scene.add(mesh);
+
return mesh;
+
});
+
+
// Connection lines - pre-render all cylinders
+
let connectionCylinders = [];
+
+
// Pre-create all connection cylinders
+
console.log('Pre-rendering', pairs.slice(0, 1000).length, 'connections...');
+
pairs.slice(0, 1000).forEach((pair, idx) => {
+
const j1 = junctions[pair.i];
+
const j2 = junctions[pair.j];
+
+
const p1 = new THREE.Vector3(
+
(j1.x * scale) - 10,
+
(j1.y * scale) - 10,
+
(j1.z * scale) - 10
+
);
+
const p2 = new THREE.Vector3(
+
(j2.x * scale) - 10,
+
(j2.y * scale) - 10,
+
(j2.z * scale) - 10
+
);
+
+
// Create cylinder between points
+
const direction = new THREE.Vector3().subVectors(p2, p1);
+
const length = direction.length();
+
const cylinderGeometry = new THREE.CylinderGeometry(0.015, 0.015, length, 4);
+
const cylinderMaterial = new THREE.MeshBasicMaterial({
+
transparent: true,
+
opacity: 0.8
+
});
+
+
const cylinder = new THREE.Mesh(cylinderGeometry, cylinderMaterial);
+
+
// Position at midpoint
+
cylinder.position.copy(p1).add(direction.multiplyScalar(0.5));
+
+
// Orient cylinder to connect the two points
+
cylinder.quaternion.setFromUnitVectors(
+
new THREE.Vector3(0, 1, 0),
+
direction.normalize()
+
);
+
+
// Initially invisible
+
cylinder.visible = false;
+
cylinder.userData.pairIndex = idx;
+
cylinder.userData.i = pair.i;
+
cylinder.userData.j = pair.j;
+
+
scene.add(cylinder);
+
connectionCylinders.push(cylinder);
+
});
+
console.log('Pre-rendering complete!');
+
+
// Pre-generate stable colors for each junction based on their index
+
const junctionColors = [];
+
const hues = [];
+
for (let i = 0; i < junctions.length; i++) {
+
const hue = (i * 137.508) % 360; // Golden angle for good distribution
+
junctionColors.push(new THREE.Color().setHSL(hue / 360, 0.8, 0.55));
+
}
+
+
function updateConnections(stage) {
+
const numConnections = stage.connections.length;
+
+
if (numConnections === 0) {
+
// No connections yet - make all junctions small and hide all cylinders
+
junctionMeshes.forEach(mesh => {
+
mesh.scale.set(0.33, 0.33, 0.33);
+
mesh.material = new THREE.MeshPhongMaterial({
+
color: 0x89b4fa,
+
emissive: 0x89b4fa,
+
emissiveIntensity: 0.2
+
});
+
});
+
connectionCylinders.forEach(cyl => cyl.visible = false);
+
return;
+
}
+
+
// Color junctions by circuit - use lowest junction index as stable color
+
const uf = new UnionFind(junctions.length);
+
stage.connections.forEach(conn => uf.union(conn.i, conn.j));
+
+
// Map each root to the lowest junction index in that circuit for stable colors
+
const circuitColors = new Map();
+
for (let i = 0; i < junctions.length; i++) {
+
const root = uf.find(i);
+
if (!circuitColors.has(root)) {
+
// Find the lowest index in this circuit
+
let lowestInCircuit = i;
+
for (let j = 0; j < i; j++) {
+
if (uf.find(j) === root) {
+
lowestInCircuit = j;
+
break;
+
}
+
}
+
circuitColors.set(root, junctionColors[lowestInCircuit]);
+
}
+
}
+
+
// Track which junctions are connected
+
const connectedJunctions = new Set();
+
stage.connections.forEach(conn => {
+
connectedJunctions.add(conn.i);
+
connectedJunctions.add(conn.j);
+
});
+
+
junctionMeshes.forEach((mesh, idx) => {
+
const root = uf.find(idx);
+
const color = circuitColors.get(root);
+
+
mesh.material = new THREE.MeshPhongMaterial({
+
color: color,
+
emissive: color,
+
emissiveIntensity: 0.4
+
});
+
+
// Make unconnected junctions smaller
+
if (connectedJunctions.has(idx)) {
+
mesh.scale.set(1, 1, 1);
+
} else {
+
mesh.scale.set(0.33, 0.33, 0.33);
+
}
+
});
+
+
// Show/hide cylinders and update their colors based on current stage
+
connectionCylinders.forEach((cylinder, idx) => {
+
if (idx < numConnections) {
+
cylinder.visible = true;
+
// Update color to match circuit
+
const root = uf.find(cylinder.userData.i);
+
const lineColor = circuitColors.get(root);
+
cylinder.material.color = lineColor;
+
} else {
+
cylinder.visible = false;
+
}
+
});
+
}
+
+
// Animation state
+
let currentStage = 0;
+
let isPlaying = false;
+
let lastTime = 0;
+
+
const playBtn = document.getElementById('play');
+
const prevBtn = document.getElementById('prev');
+
const nextBtn = document.getElementById('next');
+
const resetBtn = document.getElementById('reset');
+
const speedSlider = document.getElementById('speed');
+
const stepInfo = document.getElementById('stepInfo');
+
const statsInfo = document.getElementById('statsInfo');
+
+
function updateUI() {
+
const stage = stages[currentStage];
+
stepInfo.textContent = `Step: ${currentStage} / 1000`;
+
statsInfo.textContent = `Circuits: ${stage.circuits} | Largest: ${stage.largest} | Product: ${stage.product.toLocaleString()}`;
+
+
prevBtn.disabled = currentStage === 0;
+
nextBtn.disabled = currentStage === stages.length - 1;
+
+
updateConnections(stage);
+
}
+
+
function goToStage(index) {
+
currentStage = Math.max(0, Math.min(stages.length - 1, index));
+
updateUI();
+
}
+
+
prevBtn.addEventListener('click', () => goToStage(currentStage - 1));
+
nextBtn.addEventListener('click', () => goToStage(currentStage + 1));
+
resetBtn.addEventListener('click', () => goToStage(0));
+
+
playBtn.addEventListener('click', () => {
+
isPlaying = !isPlaying;
+
playBtn.textContent = isPlaying ? '⏸ Pause' : '▶ Play';
+
if (isPlaying && currentStage === stages.length - 1) {
+
goToStage(0);
+
}
+
});
+
+
document.addEventListener('keydown', (e) => {
+
if (e.key === 'ArrowLeft') prevBtn.click();
+
if (e.key === 'ArrowRight') nextBtn.click();
+
if (e.key === ' ') {
+
e.preventDefault();
+
playBtn.click();
+
}
+
if (e.key === 'r' || e.key === 'R') resetBtn.click();
+
});
+
+
window.addEventListener('resize', () => {
+
camera.aspect = window.innerWidth / window.innerHeight;
+
camera.updateProjectionMatrix();
+
renderer.setSize(window.innerWidth, window.innerHeight);
+
});
+
+
// Animation loop
+
function animate(time) {
+
requestAnimationFrame(animate);
+
+
// Auto-advance if playing
+
if (isPlaying) {
+
const speed = 1000 - parseInt(speedSlider.value);
+
if (time - lastTime > speed) {
+
if (currentStage < stages.length - 1) {
+
goToStage(currentStage + 1);
+
} else {
+
isPlaying = false;
+
playBtn.textContent = '▶ Play';
+
}
+
lastTime = time;
+
}
+
}
+
+
controls.update();
+
renderer.render(scene, camera);
+
}
+
+
// Initialize - start at step 0
+
currentStage = 0;
+
updateUI();
+
animate(0);
+
</script>
+
</body>
+
</html>