diff --git a/AoC_2025.Tests/Day08Test.cs b/AoC_2025.Tests/Day08Test.cs new file mode 100644 index 0000000..f915e80 --- /dev/null +++ b/AoC_2025.Tests/Day08Test.cs @@ -0,0 +1,47 @@ +using AdvenOfCode.Contracts; + +namespace AoC_2025.Tests; + +public class Day08Test +{ + private readonly Day08 _sut; + private static readonly string rootPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + private readonly string TestInputPath = @$"{rootPath}\AoC-PuzzleInputs\2025\Test\day08.txt"; + private readonly string ProdInputPath = @$"{rootPath}\AoC-PuzzleInputs\2025\Prod\day08.txt"; + + public Day08Test() + { + _sut = new Day08(); + } + [Fact] + public void Part01_Test_equals_40() + { + var actual = _sut.SolvePart1(TestInputPath, 10); + + Assert.Equal(40, actual); + } + + [Fact] + public void Part01_Prod_equals_42840() + { + var actual = _sut.SolvePart1(ProdInputPath, 1000); + + Assert.Equal(42840, actual); + } + + [Fact] + public void Part02_Test_equals_25272() + { + var actual = _sut.SolvePart2(TestInputPath); + + Assert.Equal(25272, actual); + } + + [Fact] + public void Part02_Prod_equals_170629052() + { + var actual = _sut.SolvePart2(ProdInputPath); + + Assert.Equal(170629052, actual); + } +} \ No newline at end of file diff --git a/AoC_2025/Day08.cs b/AoC_2025/Day08.cs new file mode 100644 index 0000000..ace1964 --- /dev/null +++ b/AoC_2025/Day08.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Generic; +using System.Data.Common; +using System.IO; +using System.Linq; +using AdvenOfCode.Contracts; +using Coordinate = (int x, int y, int z); + +namespace AoC_2025; + +public class Day08 +{ + private Coordinate[] ParsePuzzleInput(string path) + { + var puzzleInput = File.ReadAllLines(path) + .Where(line => !string.IsNullOrWhiteSpace(line)) + .Select(line => line.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(str => int.Parse(str)).ToArray()) + .Select(numbers => (numbers[0], numbers[1], numbers[2])) + .ToArray(); + return puzzleInput; + } + + public long SolvePart1(string pathToPuzzleInput, int amountToConnect) + { + var points = ParsePuzzleInput(pathToPuzzleInput); + Dictionary> connected = points.ToDictionary(p => p, p => new HashSet()); + var pointsWithClosest = points.Select(point => + { + var closest = GetClosestPoint(point, points, connected); + return (point, closest: closest.point, closest.distance); + }) + .OrderBy(x => x.distance) + .ToArray(); + + List> circuits = []; + for (var i = 0; i < amountToConnect; i++) + { + var next = pointsWithClosest[0]; + _ = AddToCircuits(next.point, next.closest, circuits); + connected[next.point].Add(next.closest); + connected[next.closest].Add(next.point); + pointsWithClosest = points.Select(point => + { + var closest = GetClosestPoint(point, points, connected); + return (point, closest: closest.point, closest.distance); + }) + .OrderBy(x => x.distance) + .ToArray(); + } + + return circuits + .Select(circuit => circuit.Count) + .OrderDescending() + .Take(3) + .Aggregate((acc, next) => acc * next); + } + + public long SolvePart2(string pathToPuzzleInput) + { + var points = ParsePuzzleInput(pathToPuzzleInput); + Dictionary> connected = points.ToDictionary(p => p, p => new HashSet()); + var pointsWithClosest = points.Select(point => + { + var closest = GetClosestPoint(point, points, connected); + return (point, closest: closest.point, closest.distance); + }) + .OrderBy(x => x.distance) + .ToArray(); + + List> circuits = points.Select(p => new List() { p }).ToList(); + (Coordinate a, Coordinate b) lastConnected = ((0, 0, 0), (0, 0, 0)); + while(circuits.Count > 1) + { + var next = pointsWithClosest[0]; + _ = AddToCircuits(next.point, next.closest, circuits); + connected[next.point].Add(next.closest); + connected[next.closest].Add(next.point); + lastConnected = (next.point, next.closest); + pointsWithClosest = points.Select(point => + { + var closest = GetClosestPoint(point, points, connected); + return (point, closest: closest.point, closest.distance); + }) + .OrderBy(x => x.distance) + .ToArray(); + } + + return lastConnected.a.x * lastConnected.b.x; + } + + private (Coordinate point, double distance) GetClosestPoint(Coordinate a, Coordinate[] points, Dictionary> connections) + { + var minDistance = double.MaxValue; + var minIndex = -1; + for (var i = 0; i < points.Length; i++) + { + if(a == points[i]) continue; + if(connections[a].Contains(points[i])) continue; + + var distance = GetDistance(a, points[i]); + if (minDistance > distance) + { + minIndex = i; + minDistance = GetDistance(a, points[i]); + } + } + + return (points[minIndex], minDistance); + } + + private double GetDistance(Coordinate a, Coordinate b) + { + return Math.Sqrt(Math.Pow(a.x - b.x, 2) + Math.Pow(a.y - b.y, 2) + Math.Pow(a.z - b.z, 2)); + } + + private int AddToCircuits(Coordinate a, Coordinate b, List> circuits) + { + var circuitToAddBTo = circuits.FirstOrDefault(circuit => circuit.Contains(a)); + var circuitToAddATo = circuits.FirstOrDefault(circuit => circuit.Contains(b)); + if (circuitToAddATo == null && circuitToAddBTo == null) + { + circuits.Add([a, b]); + return 2; + } + + if (circuitToAddATo == circuitToAddBTo) + { + return 0; + } + + if (circuitToAddATo != null && circuitToAddBTo != null) + { + circuits.Remove(circuitToAddATo); + circuits.Remove(circuitToAddBTo); + circuits.Add([..circuitToAddATo, ..circuitToAddBTo]); + return 0; + } + + if (circuitToAddATo != null) + { + circuitToAddATo.Add(a); + } + else + { + circuitToAddBTo.Add(b); + } + + return 1; + } + + private IEnumerable<(Coordinate pointA, Coordinate pointB, double distance)> GetAllDistances(Coordinate[] coordinates) + { + var combinations = coordinates + .SelectMany((coord, ix) => + coordinates.Skip(ix + 1).Select(coordB => (coord, coordB, distance: GetDistance(coord, coordB)))) + .OrderBy(x => x.distance) + .ToArray(); + return combinations; + } +} \ No newline at end of file