diff --git a/06/WaitForIt.Tests/ProgramTests.cs b/06/WaitForIt.Tests/ProgramTests.cs new file mode 100644 index 0000000..0159c46 --- /dev/null +++ b/06/WaitForIt.Tests/ProgramTests.cs @@ -0,0 +1,16 @@ +namespace WaitForIt.Tests; + +public class ProgramTests +{ + public void Main_WhenGivenInput_ItShouldReturnTheProductOfTheMarginOfErrors() + { + var result = Program.Main(["INPUT.txt"]); + result.Should().Be(1710720); + } + + public void Main_WhenGivenInputAndPart2_ItShouldReturnTheNumberOfWaysToWin() + { + var result = Program.Main(["INPUT.txt", "part2"]); + result.Should().Be(35349468); + } +} \ No newline at end of file diff --git a/06/WaitForIt.Tests/PuzzleParserTests.cs b/06/WaitForIt.Tests/PuzzleParserTests.cs new file mode 100644 index 0000000..c992a3e --- /dev/null +++ b/06/WaitForIt.Tests/PuzzleParserTests.cs @@ -0,0 +1,38 @@ +namespace WaitForIt.Tests; + +public class PuzzleParserTests +{ + private readonly PuzzleParser _sut = new(); + + [Theory] + [MemberData(nameof(TestData.ValidInputData), MemberType = typeof(TestData))] + public void Parse_WhenGivenValidInput_ItShouldReturnAListOfRaces(string[] input, List expected) + { + var result = _sut.ParseRaces(input); + result.Should().BeEquivalentTo(expected); + } + + public static class TestData + { + private static readonly string[] ValidInput = + [ + "Time: 7 15 30", + "Distance: 9 40 200" + ]; + + public static IEnumerable ValidInputData => + new List + { + new object[] + { + ValidInput, + new List + { + new(7, 9), + new(15, 40), + new(30, 200) + } + } + }; + } +} \ No newline at end of file diff --git a/06/WaitForIt.Tests/RaceTests.cs b/06/WaitForIt.Tests/RaceTests.cs new file mode 100644 index 0000000..91c33ad --- /dev/null +++ b/06/WaitForIt.Tests/RaceTests.cs @@ -0,0 +1,14 @@ +namespace WaitForIt.Tests; + +public class RaceTests +{ + [Theory] + [InlineData(7, 9, 4)] + [InlineData(15, 40, 8)] + [InlineData(30, 200, 9)] + public void CalculateNumberOfWaysToWin_WhenCalled_ItShouldReturnNumberOfWaysToWin(int raceDuration, int distanceRecord, int expected) + { + var result = new Race(raceDuration, distanceRecord).CalculateNumberOfWaysToWin(); + result.Should().Be(expected); + } +} \ No newline at end of file diff --git a/06/WaitForIt.Tests/UnitTest1.cs b/06/WaitForIt.Tests/UnitTest1.cs deleted file mode 100644 index 91eaf69..0000000 --- a/06/WaitForIt.Tests/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace WaitForIt.Tests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } -} \ No newline at end of file diff --git a/06/WaitForIt.Tests/WaitForIt.Tests.csproj b/06/WaitForIt.Tests/WaitForIt.Tests.csproj index 6a16e89..0647535 100644 --- a/06/WaitForIt.Tests/WaitForIt.Tests.csproj +++ b/06/WaitForIt.Tests/WaitForIt.Tests.csproj @@ -27,4 +27,10 @@ + + + PreserveNewest + + + diff --git a/06/WaitForIt/Program.cs b/06/WaitForIt/Program.cs index 3751555..c6c8015 100644 --- a/06/WaitForIt/Program.cs +++ b/06/WaitForIt/Program.cs @@ -1,2 +1,127 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); +namespace WaitForIt; + +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 parser = new PuzzleParser(); + var input = await File.ReadAllLinesAsync(args[0]); + + long result = args.Length > 1 && args[1] == "part2" + ? parser + .ParseRace(input) + .CalculateNumberOfWaysToWin() + : parser.ParseRaces(input) + .Select(r => r.CalculateNumberOfWaysToWin()) + .Aggregate((long)1, (acc, curr) => acc * curr); + + Console.WriteLine($"The product of the margin of errors is {result}."); + + return (int)result; + } +} + +/// +/// Parses the puzzle input. +/// +public class PuzzleParser +{ + private List GetValues(string input) => input + .Split(' ', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries) + .Skip(1) + .Select(long.Parse) + .ToList(); + + /// + /// Parses the puzzle input to list of races + /// + /// The list of races + /// A list containing instances of . + public List ParseRaces(string[] racesInput) + { + var races = new List(); + var durations = GetValues(racesInput[0]); + var distances = GetValues(racesInput[1]); + + if (durations.Count != distances.Count) + { + throw new ArgumentException("The number of times and distances must be equal."); + } + + for (int i = 0; i < durations.Count; i++) + { + races.Add(new Race(durations[i], distances[i])); + } + + return races; + } + + /// + /// Parses the puzzle input as single race + /// + /// The list of races + /// An instance of . + public Race ParseRace(string[] racesInput) + { + var duration = string.Join("", GetValues(racesInput[0]).Select(v => v.ToString())); + var distance = string.Join("", GetValues(racesInput[1]).Select(v => v.ToString())); + + return new Race(long.Parse(duration), long.Parse(distance)); + } +} + +/// +/// Represents a Race +/// +/// The duration of the race +/// The distance record +/// An instance of . +public class Race( + long duration, + long distanceRecord +) +{ + /// + /// Gets the duration of the race + /// + public long Duration { get; init; } = duration; + + /// + /// Gets the distance record + /// + public long DistanceRecord { get; init; } = distanceRecord; + + /// + /// Calculates the number of ways the race can be won. + /// + /// The number of ways the race can be won. + public long CalculateNumberOfWaysToWin() + { + var numberOfWaysToWin = 0; + + for (var secsHeld = 0; secsHeld < Duration; secsHeld++) + { + var speed = 1 * secsHeld; + var distance = speed * (Duration - secsHeld); + + if (distance > DistanceRecord) + { + numberOfWaysToWin++; + } + } + + return numberOfWaysToWin; + } +} \ No newline at end of file