From e7af72913a2030c2f543b6bbf5f65ac9ab03db47 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Sat, 16 Dec 2023 20:36:09 -0600 Subject: [PATCH] feat: solve part 1 and part 2 --- .../CosmicExpansion.Tests.csproj | 6 + 11/CosmicExpansion.Tests/UnitTest1.cs | 10 - 11/CosmicExpansion.Tests/UniverseTests.cs | 143 +++++++++++++++ 11/CosmicExpansion/Program.cs | 173 +++++++++++++++++- 4 files changed, 320 insertions(+), 12 deletions(-) delete mode 100644 11/CosmicExpansion.Tests/UnitTest1.cs create mode 100644 11/CosmicExpansion.Tests/UniverseTests.cs diff --git a/11/CosmicExpansion.Tests/CosmicExpansion.Tests.csproj b/11/CosmicExpansion.Tests/CosmicExpansion.Tests.csproj index 74d97f9..08f293c 100644 --- a/11/CosmicExpansion.Tests/CosmicExpansion.Tests.csproj +++ b/11/CosmicExpansion.Tests/CosmicExpansion.Tests.csproj @@ -27,4 +27,10 @@ + + + PreserveNewest + + + diff --git a/11/CosmicExpansion.Tests/UnitTest1.cs b/11/CosmicExpansion.Tests/UnitTest1.cs deleted file mode 100644 index f54379d..0000000 --- a/11/CosmicExpansion.Tests/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace CosmicExpansion.Tests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} \ No newline at end of file diff --git a/11/CosmicExpansion.Tests/UniverseTests.cs b/11/CosmicExpansion.Tests/UniverseTests.cs new file mode 100644 index 0000000..e99b316 --- /dev/null +++ b/11/CosmicExpansion.Tests/UniverseTests.cs @@ -0,0 +1,143 @@ +namespace CosmicExpansion.Tests; + +public class UniverseTests +{ + [Theory] + [MemberData(nameof(TestData.ParseTestData), MemberType = typeof(TestData))] + public void Parse_WhenGivenUniverseWithGalaxies_ItShouldReturnListOfGalaxies(string[] givenInput, Universe expectedUniverse) + { + var result = Universe.Parse(givenInput); + result.Should().BeEquivalentTo(expectedUniverse); + } + + [Theory] + [MemberData(nameof(TestData.GetShortestPathsBetweenGalaxiesTestData), MemberType = typeof(TestData))] + public void GetShortestPathsBetweenGalaxies_WhenGivenUniverseWithGalaxies_ItShouldReturnShortestPathsBetweenGalaxies(Universe givenUniverse, int expectedNumberOrPaths) + { + var result = givenUniverse.GetShortestPathsBetweenGalaxies(); + result.Count.Should().Be(expectedNumberOrPaths); + } + + [Theory] + [MemberData(nameof(TestData.SumOfShortestPathsBetweenGalaxiesTestData), MemberType = typeof(TestData))] + public void SumOfShortestPathsBetweenGalaxies_WhenGivenUniverseWithGalaxies_ItShouldReturnSumOfShortestPathsBetweenGalaxies(Universe givenUniverse, int expectedSumOfShortestPaths) + { + givenUniverse.SumOfShortestPathsBetweenGalaxies.Should().Be(expectedSumOfShortestPaths); + } + + [Theory] + [MemberData(nameof(TestData.CalculateDistanceBetweenGalaxiesTestData), MemberType = typeof(TestData))] + public void CalculateDistanceBetweenGalaxies_WhenGivenTwoGalaxies_ItShouldReturnDistanceBetweenGalaxies(Galaxy startGalaxy, Galaxy endGalaxy, int expectedDistance) + { + var result = Universe.CalculateDistanceBetweenGalaxies(startGalaxy, endGalaxy); + result.Should().Be(expectedDistance); + } + + [Fact] + public void SumOfShortestPathsBetweenGalaxies_WhenGivenInput_ItShouldReturnSumOfShortestPathsBetweenGalaxies() + { + var input = File.ReadAllLines("INPUT.txt"); + var universe = Universe.Parse(input); + universe.SumOfShortestPathsBetweenGalaxies.Should().Be(10292708); + } + + [Fact] + public void SumOfShortestPathsBetweenGalaxies_WhenGivenInputAndIsPart2_ItShouldReturnSumOfShortestPathsBetweenGalaxies() + { + var input = File.ReadAllLines("INPUT.txt"); + var universe = Universe.Parse(input, 1_000_000); + universe.SumOfShortestPathsBetweenGalaxies.Should().Be(790194712336); + } + + + + public static class TestData + { + private static readonly Universe TestUniverse = new( + 12, + 13, + [ + new(0, 4), + new(1, 9), + new(2, 0), + new(5, 8), + new(6, 1), + new(7, 12), + new(10, 9), + new(11, 0), + new(11, 5), + ] + ); + + public static IEnumerable ParseTestData => + new List + { + new object[] + { + new string[] + { + "...#......", + ".......#..", + "#.........", + "..........", + "......#...", + ".#........", + ".........#", + "..........", + ".......#..", + "#...#.....", + }, + TestUniverse, + } + }; + + public static IEnumerable GetShortestPathsBetweenGalaxiesTestData => + new List + { + new object[] + { + TestUniverse, + 36, + } + }; + + public static IEnumerable SumOfShortestPathsBetweenGalaxiesTestData => + new List + { + new object[] + { + TestUniverse, + 374, + } + }; + + public static IEnumerable CalculateDistanceBetweenGalaxiesTestData => + new List + { + new object[] + { + TestUniverse.Galaxies[4], + TestUniverse.Galaxies[8], + 9, + }, + new object[] + { + TestUniverse.Galaxies[0], + TestUniverse.Galaxies[6], + 15, + }, + new object[] + { + TestUniverse.Galaxies[2], + TestUniverse.Galaxies[5], + 17, + }, + new object[] + { + TestUniverse.Galaxies[7], + TestUniverse.Galaxies[8], + 5, + } + }; + } +} \ No newline at end of file diff --git a/11/CosmicExpansion/Program.cs b/11/CosmicExpansion/Program.cs index 3751555..137ec62 100644 --- a/11/CosmicExpansion/Program.cs +++ b/11/CosmicExpansion/Program.cs @@ -1,2 +1,171 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +using System.Diagnostics; + +namespace CosmicExpansion; + +public class Program +{ + public static async Task Main(string[] args) + { + if (args.Length is 0) + { + Console.WriteLine("Please provide a path to the input file."); + return -1; + } + + if (File.Exists(args[0]) is false) + { + Console.WriteLine("The provided file does not exist."); + return -2; + } + + var isPart2 = args.Length > 1 && args[1] == "part2"; + var input = await File.ReadAllLinesAsync(args[0]); + + var stopwatch = new Stopwatch(); + stopwatch.Start(); + + var universe = isPart2 + ? Universe.Parse(input, 1_000_000) + : Universe.Parse(input); + + var result = universe.SumOfShortestPathsBetweenGalaxies; + + stopwatch.Stop(); + + Console.WriteLine($"The sum of the shortest paths between galaxies is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); + + return (int)result; + } +} + +public class Universe( + int height, + int width, + List galaxies +) +{ + private const char GalaxySymbol = '#'; + public int Height { get; init; } = height; + public int Width { get; init; } = width; + public List Galaxies { get; init; } = galaxies; + public long SumOfShortestPathsBetweenGalaxies => + GetShortestPathsBetweenGalaxies() + .Values + .Sum(); + + public Dictionary<(int galaxyOneIndex, int galaxyTwoIndex), long> GetShortestPathsBetweenGalaxies() + { + var shortestPaths = new Dictionary<(int galaxyOneIndex, int galaxyTwoIndex), long>(); + + for (int i = 0; i < Galaxies.Count; i++) + { + var galaxyOne = Galaxies[i]; + + for (int j = i + 1; j < Galaxies.Count; j++) + { + var galaxyTwo = Galaxies[j]; + + if (shortestPaths.ContainsKey((i, j)) || shortestPaths.ContainsKey((j, i))) + { + continue; + } + + var shortestPath = CalculateDistanceBetweenGalaxies(galaxyOne, galaxyTwo); + shortestPaths.Add((i, j), shortestPath); + } + } + + return shortestPaths; + } + + public static long CalculateDistanceBetweenGalaxies(Galaxy galaxyOne, Galaxy galaxyTwo) => + Math.Abs(galaxyTwo.Row - galaxyOne.Row) + Math.Abs(galaxyTwo.Column - galaxyOne.Column); + + public static Universe Parse(string[] input, int expandFactor = 2) + { + var emptyRows = new List(); + var emptyColumns = new List(); + var galaxies = new List(); + + for (int i = 0; i < input.Length; i++) + { + var currentRow = input[i]; + + if (currentRow.Contains(GalaxySymbol) is false) + { + emptyRows.Add(i); + } + } + + for (int i = 0; i < input[0].Length; i++) + { + var galaxyInColumn = false; + + for (int j = 0; j < input.Length; j++) + { + var currentPosition = input[j][i]; + + if (currentPosition == GalaxySymbol) + { + galaxyInColumn = true; + break; + } + } + + if (galaxyInColumn is false) + { + emptyColumns.Add(i); + } + } + + for (int i = 0; i < input.Length; i++) + { + var currentRow = input[i]; + + for (int j = 0; j < currentRow.Length; j++) + { + var currentPosition = currentRow[j]; + + if (currentPosition == GalaxySymbol) + { + var numOfEmptyRowsBefore = emptyRows.Where(row => row < i).Count(); + var rowOffset = numOfEmptyRowsBefore is 0 + ? 0 + : (numOfEmptyRowsBefore * expandFactor) - numOfEmptyRowsBefore; + + var numOfEmptyColumnsBefore = emptyColumns.Where(column => column < j).Count(); + var columnOffset = numOfEmptyColumnsBefore is 0 + ? 0 + : (numOfEmptyColumnsBefore * expandFactor) - numOfEmptyColumnsBefore; + + galaxies.Add(new Galaxy(i + rowOffset, j + columnOffset)); + } + } + } + + var heightOffset = emptyRows.Count is 0 + ? 0 + : (emptyRows.Count * expandFactor) - emptyRows.Count; + var widthOffset = emptyColumns.Count is 0 + ? 0 + : (emptyColumns.Count * expandFactor) - emptyColumns.Count; + + var height = input.Length + heightOffset; + var width = input[0].Length + widthOffset; + + return new Universe( + height, + width, + galaxies + ); + } +} + +public class Galaxy( + long row, + long column +) +{ + public long Row { get; init; } = row; + public long Column { get; init; } = column; +} \ No newline at end of file