diff --git a/advent-of-code-2021.csproj b/advent-of-code-2021.csproj
index da056ec..d2794ff 100644
--- a/advent-of-code-2021.csproj
+++ b/advent-of-code-2021.csproj
@@ -72,6 +72,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/inputs/19.txt b/inputs/19.txt
new file mode 100644
index 0000000..d8699df
--- /dev/null
+++ b/inputs/19.txt
@@ -0,0 +1,856 @@
+--- scanner 0 ---
+536,703,543
+-132,-41,-64
+-429,350,-809
+-477,567,455
+-369,-732,-732
+-806,-680,810
+502,729,469
+-417,400,-831
+497,-739,-564
+-545,-726,-743
+271,357,-605
+13,-26,94
+508,-654,-647
+-581,-685,774
+396,-908,355
+-481,568,418
+-634,-665,910
+306,410,-668
+540,739,539
+465,-656,-423
+-474,-628,-795
+-463,568,426
+254,306,-662
+358,-788,353
+-396,313,-691
+251,-873,452
+
+--- scanner 1 ---
+405,377,-919
+-471,604,350
+479,506,644
+495,326,707
+-407,-663,-846
+618,-537,318
+-584,506,-618
+-577,-375,640
+682,-442,283
+-579,-587,598
+612,361,-928
+-253,-680,-729
+512,382,-895
+531,379,751
+-468,-460,659
+-262,-699,-913
+974,-376,-550
+539,-333,326
+917,-437,-630
+-422,787,372
+48,7,-59
+-451,618,404
+-697,390,-626
+-766,485,-587
+971,-556,-663
+
+--- scanner 2 ---
+-652,-598,521
+680,708,-671
+401,-847,-328
+-666,588,522
+-629,-292,-571
+256,-750,-363
+-576,-428,-551
+-698,690,-828
+640,385,384
+698,679,-614
+-801,-392,-556
+490,-475,680
+-594,-543,549
+27,-15,66
+-878,546,524
+-703,476,-864
+741,661,-790
+-793,527,633
+506,-675,668
+-736,691,-860
+-646,-597,737
+650,579,455
+363,-849,-448
+-77,-44,-102
+636,387,485
+609,-565,657
+
+--- scanner 3 ---
+-694,-480,675
+-79,48,-39
+-713,754,292
+329,-686,-566
+352,-717,-653
+689,385,454
+-736,862,-562
+-619,615,350
+687,515,-478
+754,359,540
+-741,-551,707
+-416,-641,-826
+780,-406,714
+757,427,547
+-766,670,-548
+376,-703,-591
+-676,-675,645
+616,-364,628
+-413,-651,-750
+-645,601,324
+-837,864,-558
+618,546,-543
+651,-444,702
+-438,-700,-839
+735,493,-571
+
+--- scanner 4 ---
+-530,746,312
+786,-443,556
+-889,-559,548
+707,-625,-700
+-597,746,-565
+-695,733,324
+797,-456,613
+521,468,-520
+798,-448,530
+-820,-811,-882
+-905,-712,-989
+700,666,439
+759,795,432
+637,796,456
+-877,-533,314
+-887,-526,362
+671,-621,-704
+-575,696,330
+-940,-809,-844
+-478,699,-504
+-654,717,-547
+-56,111,-123
+529,444,-427
+618,407,-518
+697,-695,-738
+
+--- scanner 5 ---
+-622,-664,638
+91,56,-46
+-584,-588,503
+-597,-542,-844
+637,-732,382
+541,-389,-695
+608,849,773
+-824,472,329
+-715,664,-901
+-807,472,398
+696,824,658
+-748,513,423
+734,-704,553
+-603,-571,-996
+702,-641,519
+-564,648,-902
+-546,-560,-802
+529,757,-875
+-774,670,-893
+710,934,780
+-633,-647,616
+45,186,-187
+571,805,-747
+469,-300,-758
+548,722,-801
+470,-365,-800
+
+--- scanner 6 ---
+-63,90,49
+498,853,-791
+578,-569,-624
+-655,-638,533
+625,-555,-405
+612,-558,-546
+481,703,-791
+472,508,699
+-676,-594,581
+641,-783,554
+689,489,688
+-597,648,491
+-588,829,-432
+447,-795,466
+-611,-448,-552
+-628,702,-523
+517,666,-715
+-730,-425,-642
+-606,719,-512
+-714,-437,-442
+-537,611,499
+-462,665,425
+597,478,703
+460,-784,616
+-648,-556,677
+
+--- scanner 7 ---
+845,881,-301
+-501,-349,486
+-315,-608,-344
+-480,685,-388
+718,980,-308
+915,979,627
+-279,-529,-320
+749,-436,411
+-508,-323,638
+-557,-346,709
+744,-481,476
+738,-636,412
+-431,775,-306
+-486,759,-520
+378,-599,-409
+-575,569,537
+538,-579,-380
+94,37,37
+-656,696,463
+931,951,534
+931,865,728
+448,-570,-525
+-739,579,511
+-301,-469,-277
+782,925,-452
+
+--- scanner 8 ---
+-642,587,781
+449,768,-502
+-541,-248,454
+-652,688,-666
+-657,-301,345
+388,565,306
+-711,575,-617
+-597,592,716
+31,60,-133
+-705,-303,534
+-728,553,-628
+518,471,325
+-449,-629,-686
+-462,-608,-643
+399,395,424
+573,-579,-519
+-49,145,4
+485,-560,410
+365,-564,556
+408,793,-443
+-580,-592,-693
+542,-671,-541
+408,862,-426
+380,-463,470
+-442,598,780
+580,-773,-513
+
+--- scanner 9 ---
+511,-520,557
+831,490,487
+-549,-659,-680
+536,-615,-490
+165,-84,33
+652,-552,474
+-516,602,-544
+-439,-556,-691
+-267,-451,305
+-822,579,548
+-571,544,-572
+-16,-6,-106
+841,452,-616
+-517,596,-760
+-364,-306,340
+584,-594,-534
+820,314,481
+680,-622,-572
+883,419,585
+-722,751,533
+-639,-540,-703
+735,-563,559
+947,393,-650
+-686,526,528
+905,520,-588
+-265,-413,425
+
+--- scanner 10 ---
+-737,650,-388
+799,760,694
+-389,-453,768
+-667,-803,-543
+537,-419,977
+573,-564,910
+699,461,-254
+-644,698,-366
+-657,-874,-737
+556,-410,793
+-672,581,-419
+-653,-773,-735
+680,814,727
+78,-167,23
+-376,-475,827
+861,-734,-576
+649,-736,-534
+776,-625,-595
+-649,292,813
+860,817,614
+-624,339,830
+50,-20,162
+-465,-450,778
+-562,315,844
+903,382,-262
+794,525,-219
+
+--- scanner 11 ---
+99,76,34
+-676,680,608
+-416,-818,449
+-383,-733,-582
+-278,-673,-485
+-263,-587,-586
+-297,-719,373
+610,574,866
+544,-363,576
+743,689,-672
+-817,391,-552
+-607,706,535
+601,429,776
+-805,494,-602
+-779,332,-498
+699,-427,570
+703,798,-570
+759,-798,-323
+643,-689,-270
+-605,648,465
+648,653,-655
+-286,-837,543
+-21,-98,124
+581,458,936
+807,-609,-325
+744,-362,565
+
+--- scanner 12 ---
+-425,-551,749
+399,-500,-745
+621,-564,617
+458,551,789
+-771,404,712
+-635,-782,-410
+141,-60,-11
+-407,-500,691
+427,311,-461
+434,438,-362
+-786,391,773
+-533,-750,-406
+578,374,-417
+420,485,830
+376,378,782
+-602,225,-477
+535,-509,559
+-733,594,734
+652,-499,714
+-466,-660,638
+375,-461,-524
+-501,241,-363
+13,-15,-141
+392,-416,-592
+-585,-935,-464
+-408,225,-391
+
+--- scanner 13 ---
+-869,-562,284
+-626,-660,-761
+863,541,-592
+-615,-577,-676
+594,-454,-831
+-713,476,667
+915,554,-452
+621,-462,713
+528,750,450
+680,-516,627
+-640,407,641
+-783,478,609
+866,578,-609
+-705,797,-876
+-875,786,-758
+662,-561,828
+-658,-801,-678
+524,593,486
+-852,-619,335
+-799,667,-857
+-817,-556,388
+479,-362,-887
+490,705,572
+101,-76,-131
+-40,-1,16
+414,-474,-816
+
+--- scanner 14 ---
+441,817,-678
+-598,650,753
+371,-553,781
+-510,-749,-608
+9,-9,185
+364,-412,661
+-698,-767,-537
+-695,787,-546
+-93,144,106
+676,-367,-546
+-564,746,-667
+478,752,-535
+-588,-350,394
+565,773,-725
+-587,-306,537
+-678,698,-692
+-773,-762,-605
+-393,632,689
+597,749,893
+827,-282,-517
+-490,578,681
+380,-405,668
+808,-318,-662
+578,692,850
+579,917,825
+-578,-442,497
+
+--- scanner 15 ---
+711,434,636
+-814,919,616
+-367,796,-569
+667,-597,612
+637,-360,-736
+668,-381,618
+699,-371,-662
+-351,789,-562
+591,759,-398
+786,439,704
+128,62,-70
+-793,-527,-586
+722,-569,670
+-754,-553,694
+600,895,-438
+601,851,-295
+-796,-491,821
+-663,834,645
+-795,883,761
+-690,-470,-654
+-728,-676,-612
+-690,-525,762
+639,-287,-734
+-358,648,-682
+523,452,707
+
+--- scanner 16 ---
+509,-793,667
+878,553,565
+-582,760,-790
+-599,-400,-777
+581,-549,-837
+-498,-416,-891
+-405,-400,-863
+-530,575,-848
+769,655,525
+-8,-11,-79
+538,-953,687
+90,-139,81
+-529,308,461
+402,325,-509
+-282,-582,524
+-347,-587,569
+654,-883,658
+-718,306,509
+-609,253,625
+-283,-593,724
+-632,607,-844
+430,-486,-793
+395,241,-662
+437,-613,-892
+733,638,620
+378,286,-730
+
+--- scanner 17 ---
+754,602,-677
+-722,-580,-369
+-439,722,669
+-372,731,-647
+16,-1,-34
+164,118,125
+433,-796,-390
+474,-459,828
+943,619,739
+-765,-605,576
+-402,923,-633
+697,631,-695
+873,649,-738
+-700,-639,-356
+592,-518,899
+-756,-789,557
+566,-719,-309
+475,-540,761
+-725,-590,-445
+692,-807,-381
+-611,-722,593
+-550,783,796
+911,620,701
+-441,764,881
+-301,925,-637
+947,834,706
+
+--- scanner 18 ---
+113,-179,93
+-709,565,-575
+-585,-728,588
+812,527,-496
+-496,315,704
+-367,364,615
+939,-532,762
+727,472,-351
+701,704,557
+-312,-886,-799
+944,-633,873
+-779,594,-691
+-533,-891,548
+499,-602,-827
+-608,-769,451
+-375,-930,-648
+-619,574,-720
+829,432,-460
+460,-823,-851
+757,693,547
+415,-755,-834
+950,-664,733
+-439,-985,-792
+702,691,743
+-341,400,702
+
+--- scanner 19 ---
+765,-723,-476
+-795,553,792
+640,682,-495
+138,123,77
+648,-491,767
+617,736,-578
+-556,-542,581
+-679,-488,-787
+-738,-463,-828
+-755,-394,-828
+508,578,559
+779,-845,-366
+-638,-549,725
+489,795,570
+611,-431,759
+665,-356,833
+478,703,555
+80,10,-59
+-594,-570,677
+-783,747,704
+-705,828,-630
+677,-838,-451
+-784,566,642
+-495,837,-652
+598,810,-543
+-646,836,-636
+
+--- scanner 20 ---
+698,-671,896
+-629,-578,570
+882,-690,887
+633,-486,-344
+815,-654,742
+862,582,-466
+-307,396,-533
+-671,-366,572
+556,494,383
+-327,471,-605
+-403,796,727
+-526,-508,-812
+-690,-451,681
+-48,50,4
+595,399,452
+-741,-541,-816
+-435,627,698
+-381,613,678
+611,-348,-461
+-527,-546,-780
+587,515,565
+887,614,-414
+-361,524,-418
+552,-419,-343
+867,468,-504
+
+--- scanner 21 ---
+591,695,-643
+-461,-434,378
+876,-525,845
+447,688,-725
+-609,715,615
+858,-320,-565
+-724,-437,365
+752,-379,-599
+725,-496,760
+816,-447,-627
+413,806,524
+-598,737,632
+-528,629,-524
+-660,-471,-643
+-649,-505,360
+405,746,561
+-606,-523,-455
+-605,-529,-671
+-638,732,-535
+339,881,578
+-110,154,-71
+857,-380,809
+38,67,-3
+-518,755,742
+495,667,-681
+-728,613,-512
+
+--- scanner 22 ---
+417,869,-670
+-390,-523,-643
+668,-601,466
+-449,460,555
+-411,652,-704
+757,407,838
+-332,-429,-504
+-399,817,-717
+-14,26,-96
+-635,458,588
+549,-578,556
+-401,470,562
+-674,-468,562
+-687,-471,703
+-364,-497,-657
+460,693,-688
+508,-376,-588
+902,442,741
+478,750,-717
+518,-305,-394
+652,-589,666
+109,117,38
+-417,772,-667
+795,527,840
+-640,-457,484
+522,-406,-396
+
+--- scanner 23 ---
+-800,-816,-415
+486,376,-362
+-411,496,636
+260,419,326
+-938,-671,519
+1,-24,20
+359,-713,278
+-930,-835,-509
+-611,650,-431
+668,-637,-556
+429,325,-345
+-833,-731,616
+423,429,-397
+282,369,338
+-953,-585,620
+400,332,305
+296,-609,383
+-437,334,532
+-919,-717,-339
+348,-707,454
+527,-720,-588
+444,-635,-599
+-540,682,-574
+-186,11,-59
+-399,393,586
+-609,708,-501
+
+--- scanner 24 ---
+661,560,-615
+-838,-878,-425
+756,-980,519
+-430,-499,528
+-840,792,-299
+-891,-834,-389
+730,337,599
+-711,491,690
+-613,-477,577
+-835,768,-406
+-827,499,803
+792,592,-663
+-711,-771,-373
+656,286,631
+-856,801,-506
+-42,-81,88
+841,216,626
+-599,-531,557
+767,-854,387
+754,-461,-726
+-732,460,872
+859,-869,436
+668,-426,-620
+626,-548,-657
+802,511,-703
+
+--- scanner 25 ---
+871,758,-518
+-441,-709,335
+458,576,380
+704,-742,-632
+-721,571,786
+-803,-452,-835
+-752,658,763
+-711,565,-686
+828,638,-529
+-687,377,-660
+392,-542,358
+-686,530,-584
+818,761,-520
+-888,-342,-883
+480,483,428
+-798,-414,-880
+324,-535,361
+-399,-623,499
+-54,-43,48
+511,548,497
+350,-449,426
+807,-776,-756
+-738,596,739
+81,137,-43
+716,-700,-860
+-479,-697,464
+
+--- scanner 26 ---
+-901,-451,391
+-534,-435,-744
+-383,810,622
+-7,71,53
+474,684,458
+-861,602,-891
+-769,-567,440
+-571,-578,-679
+-457,735,676
+605,-576,840
+-920,-596,355
+417,-505,822
+680,-508,835
+384,612,-707
+471,-446,-673
+462,781,-698
+565,644,585
+-864,517,-869
+554,666,568
+-909,737,-873
+-658,-388,-700
+-449,675,639
+585,-421,-625
+602,-567,-714
+397,746,-782
+
+--- scanner 27 ---
+-490,-562,-654
+501,516,490
+-310,-612,762
+560,738,-569
+587,-506,-667
+-782,970,-551
+-729,935,-614
+-289,-544,-609
+424,433,614
+606,-412,-491
+-833,987,-608
+378,-468,689
+-404,-503,-708
+391,546,639
+487,738,-654
+-436,705,597
+451,-491,621
+-400,701,593
+-321,-555,775
+567,-397,668
+81,40,90
+582,667,-630
+-410,739,523
+-309,-529,770
+657,-413,-678
+
+--- scanner 28 ---
+69,84,-59
+-289,-502,-551
+595,-623,583
+-456,920,-639
+583,-423,567
+778,439,713
+-576,755,732
+714,-491,-517
+-307,-690,-480
+-693,808,704
+879,437,717
+739,598,-801
+-377,912,-843
+-645,786,797
+741,640,-589
+-544,-542,669
+742,-546,-575
+-520,955,-734
+606,-513,686
+526,-545,-569
+-671,-633,709
+-561,-553,691
+-294,-427,-483
+808,631,727
+630,579,-716
+
+--- scanner 29 ---
+554,-452,430
+712,317,-664
+554,-470,422
+-577,514,-619
+-735,-733,754
+-625,-635,-349
+419,547,547
+-441,819,564
+-504,514,-704
+-518,736,440
+809,-385,-687
+30,-54,-66
+722,-406,-827
+-599,585,-680
+772,-418,-805
+-123,-145,56
+765,282,-601
+-662,-695,652
+-660,-788,804
+383,551,543
+-650,-675,-301
+-723,-747,-314
+-408,706,589
+555,-466,459
+393,513,473
+-30,28,95
+816,265,-658
+
+--- scanner 30 ---
+648,439,-720
+-883,-704,-653
+760,676,623
+681,-717,482
+288,-461,-535
+739,-762,425
+752,755,827
+-682,-622,380
+-665,-711,526
+278,-470,-658
+-587,838,467
+-793,461,-725
+588,-655,448
+-850,825,464
+-702,588,-685
+-662,843,535
+-870,-779,-787
+795,612,718
+-796,-670,405
+595,556,-748
+5,-51,26
+235,-459,-538
+-131,33,-79
+-805,-695,-822
+-713,571,-707
+626,487,-813
\ No newline at end of file
diff --git a/src/19.cs b/src/19.cs
new file mode 100644
index 0000000..2d336cc
--- /dev/null
+++ b/src/19.cs
@@ -0,0 +1,176 @@
+namespace aoc2021;
+
+internal class Day19 : Day
+{
+ internal override void Go()
+ {
+ var lines = Util.ReadAllLines("inputs/19.txt");
+ var scanners = new List>();
+ foreach (var line in lines)
+ {
+ if (line.StartsWith("--"))
+ {
+ scanners.Add(new());
+ }
+ else if (string.IsNullOrEmpty(line))
+ {
+ continue;
+ }
+ else
+ {
+ var points = line.Split(',');
+ scanners[^1].Add((Convert.ToInt32(points[0]), Convert.ToInt32(points[1]), Convert.ToInt32(points[2])));
+ }
+ }
+
+ var result = Part1(scanners);
+ Part2(result);
+ }
+
+ record struct Vector3(int X, int Y, int Z)
+ {
+ public static implicit operator Vector3((int x, int y, int z) value) => new(value.x, value.y, value.z);
+ public static Vector3 operator+(Vector3 p, Vector3 v) => (p.X + v.X, p.Y + v.Y, p.Z + v.Z);
+ public static Vector3 operator-(Vector3 p1, Vector3 p2) => (p1.X - p2.X, p1.Y - p2.Y, p1.Z - p2.Z);
+
+ public int DistanceTo(Vector3 other) => (int)(Math.Pow(other.X - X, 2) + Math.Pow(other.Y - Y, 2) + Math.Pow(other.Z - Z, 2));
+
+ public int ManhattanDistanceTo(Vector3 other)
+ {
+ var (dx, dy, dz) = this - other;
+ return Math.Abs(dx) + Math.Abs(dy) + Math.Abs(dz);
+ }
+
+ public Vector3 Transform(Vector3 up, int rotation)
+ {
+ Vector3 reoriented = up switch
+ {
+ (0, +1, 0) => (X, Y, Z),
+ (0, -1, 0) => (X, -Y, -Z),
+ (+1, 0, 0) => (Y, X, -Z),
+ (-1, 0, 0) => (Y, -X, Z),
+ (0, 0, +1) => (Y, Z, X),
+ (0, 0, -1) => (Y, -Z, -X),
+ _ => throw new Exception("Invalid up vector")
+ };
+
+ return rotation switch
+ {
+ 0 => reoriented,
+ 1 => (reoriented.Z, reoriented.Y, -reoriented.X),
+ 2 => (-reoriented.X, reoriented.Y, -reoriented.Z),
+ 3 => (-reoriented.Z, reoriented.Y, reoriented.X),
+ _ => throw new Exception("Invalid rotation")
+ };
+ }
+ }
+
+ private static readonly (int, int, int)[] Axes = new[]
+ {
+ ( 0, +1, 0),
+ ( 0, -1, 0),
+ (+1, 0, 0),
+ (-1, 0, 0),
+ ( 0, 0, +1),
+ ( 0, 0, -1)
+ };
+
+ private static (IEnumerable alignedBeacons, Vector3 translation, Vector3 up, int rotation)? Align(ICollection beacons1, ICollection beacons2)
+ {
+ // Fix beacons1, rotate beacons2
+ for (int axis = 0; axis < Axes.Length; axis++)
+ {
+ for (int rotation = 0; rotation < 4; rotation++)
+ {
+ var rotatedBeacons2 = new HashSet(beacons2.Select(b => b.Transform(Axes[axis], rotation)));
+
+ foreach (var b1 in beacons1)
+ {
+ // Assume b1 matches some b2
+ foreach (var matchingB1InB2 in rotatedBeacons2)
+ {
+ // Move all b2 so matchingB1InB2 matches b1, in scanner 1 coordinates
+ var delta = b1 - matchingB1InB2;
+ var transformedBeacons2 = rotatedBeacons2.Select(b => b + delta);
+
+ // How many overlaps did we get?
+ var intersection = new HashSet(transformedBeacons2);
+ intersection.IntersectWith(beacons1);
+ if (intersection.Count >= 12)
+ {
+ // Found the right orientation
+ return (transformedBeacons2, delta, Axes[axis], rotation);
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private static (IEnumerable> scans, IEnumerable> scanners) Reduce(IEnumerable> scans, IEnumerable> scanners)
+ {
+ var toRemove = new HashSet();
+ for (int i = 0; i < scans.Count() - 1; i++)
+ {
+ for (int j = i + 1; j < scans.Count(); j++)
+ {
+ if (toRemove.Contains(j))
+ {
+ // Already merged
+ continue;
+ }
+
+ var alignment = Align(scans.ElementAt(i), scans.ElementAt(j));
+ if (alignment != null)
+ {
+ // Convert all scanners from j coordinates to i coordinates
+ foreach (var s in scanners.ElementAt(j))
+ {
+ var scanner = alignment.Value.translation + s.Transform(alignment.Value.up, alignment.Value.rotation);
+ scanners.ElementAt(i).Add(scanner);
+ }
+ // Merge the scan sets
+ scans.ElementAt(i).UnionWith(alignment.Value.alignedBeacons);
+ toRemove.Add(j);
+ }
+ }
+ }
+ // Skip all scans and scanners that were merged
+ return (scans.Where((_, i) => !toRemove.Contains(i)), scanners.Where((_, i) => !toRemove.Contains(i)));
+ }
+
+ private static ICollection Part1(List> input)
+ {
+ using var t = new Timer();
+
+ IEnumerable> scans = input;
+ IEnumerable> scanners = input.Select((_) => new HashSet { (0, 0, 0) }).ToList();
+ while (scans.Count() > 1)
+ {
+ // Note that this will loop forever if there is no alignment
+ (scans, scanners) = Reduce(scans, scanners);
+ }
+
+ var allBeacons = scans.ElementAt(0);
+
+ t.Stop();
+ Logger.Log($"<+black>> part1: <+white>{allBeacons.Count}");
+
+ return scanners.ElementAt(0);
+ }
+
+ private static void Part2(ICollection scanners)
+ {
+ using var t = new Timer();
+
+ var scannerList = scanners.ToList();
+ var farthest =
+ Enumerable.Range(0, scannerList.Count - 1)
+ .SelectMany(i => Enumerable.Range(i + 1, scannerList.Count - i - 1).Select(j => (i, j)))
+ .Max(pair => scannerList[pair.i].ManhattanDistanceTo(scannerList[pair.j]));
+
+ t.Stop();
+ Logger.Log($"<+black>> part2: <+white>{farthest}");
+ }
+}
diff --git a/src/main.cs b/src/main.cs
index e82507d..cdbdfa9 100644
--- a/src/main.cs
+++ b/src/main.cs
@@ -37,6 +37,7 @@ else
"16" => new Day16(),
"17"=> new Day17(),
"18" => new Day18(),
+ "19" => new Day19(),
_ => new Day20(),
};
day.Go();