diff --git a/05/IYGASAF.Tests/GlobalUsings.cs b/05/IYGASAF.Tests/GlobalUsings.cs index 2d5b473..d6f0167 100644 --- a/05/IYGASAF.Tests/GlobalUsings.cs +++ b/05/IYGASAF.Tests/GlobalUsings.cs @@ -1,3 +1,3 @@ global using FluentAssertions; -global using Xunit; +global using Xunit; \ No newline at end of file diff --git a/06/WaitForIt.Tests/GlobalUsings.cs b/06/WaitForIt.Tests/GlobalUsings.cs new file mode 100644 index 0000000..2d5b473 --- /dev/null +++ b/06/WaitForIt.Tests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using FluentAssertions; + +global using Xunit; 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/WaitForIt.Tests.csproj b/06/WaitForIt.Tests/WaitForIt.Tests.csproj new file mode 100644 index 0000000..0647535 --- /dev/null +++ b/06/WaitForIt.Tests/WaitForIt.Tests.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + PreserveNewest + + + + diff --git a/06/WaitForIt/Program.cs b/06/WaitForIt/Program.cs new file mode 100644 index 0000000..e7fcf86 --- /dev/null +++ b/06/WaitForIt/Program.cs @@ -0,0 +1,135 @@ +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]); + var isPart2 = args.Length > 1 && args[1] == "part2"; + + long result = isPart2 + ? parser + .ParseRace(input) + .CalculateNumberOfWaysToWin() + : parser.ParseRaces(input) + .Select(r => r.CalculateNumberOfWaysToWin()) + .Aggregate((long)1, (acc, curr) => acc * curr); + + if (isPart2) + { + Console.WriteLine($"The number of ways to win is {result}."); + } + else + { + Console.WriteLine($"The total number of ways to win 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 diff --git a/06/WaitForIt/WaitForIt.csproj b/06/WaitForIt/WaitForIt.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/06/WaitForIt/WaitForIt.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/AdventOfCode2023.sln b/AdventOfCode2023.sln index 2c0a187..8590e39 100644 --- a/AdventOfCode2023.sln +++ b/AdventOfCode2023.sln @@ -33,6 +33,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IYGASAF", "05\IYGASAF\IYGAS EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IYGASAF.Tests", "05\IYGASAF.Tests\IYGASAF.Tests.csproj", "{CB90004D-9420-4411-9110-B50274537FE5}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "06", "06", "{86704890-D6B8-4166-853B-F1424B8396C7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WaitForIt", "06\WaitForIt\WaitForIt.csproj", "{B3D736D4-EFEE-44AA-B199-1A1C803F1793}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WaitForIt.Tests", "06\WaitForIt.Tests\WaitForIt.Tests.csproj", "{8C89E1D0-2617-4B89-BA9C-9189FC69BC43}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -82,6 +88,14 @@ Global {CB90004D-9420-4411-9110-B50274537FE5}.Debug|Any CPU.Build.0 = Debug|Any CPU {CB90004D-9420-4411-9110-B50274537FE5}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB90004D-9420-4411-9110-B50274537FE5}.Release|Any CPU.Build.0 = Release|Any CPU + {B3D736D4-EFEE-44AA-B199-1A1C803F1793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3D736D4-EFEE-44AA-B199-1A1C803F1793}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3D736D4-EFEE-44AA-B199-1A1C803F1793}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3D736D4-EFEE-44AA-B199-1A1C803F1793}.Release|Any CPU.Build.0 = Release|Any CPU + {8C89E1D0-2617-4B89-BA9C-9189FC69BC43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C89E1D0-2617-4B89-BA9C-9189FC69BC43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C89E1D0-2617-4B89-BA9C-9189FC69BC43}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C89E1D0-2617-4B89-BA9C-9189FC69BC43}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {3F8EAC09-4BC7-43AA-B72B-48DDD2710F6D} = {8C29858C-623A-461A-BF0B-254E151CD9C2} @@ -94,5 +108,7 @@ Global {FF8A5512-85FD-45E7-9F97-EC528465949C} = {522D6D1E-54AD-4479-BB78-0AB5D7493EFC} {417E0408-31C8-4945-B684-5004233D6AF4} = {5067FD6B-8F03-4502-AB83-8A391D801B47} {CB90004D-9420-4411-9110-B50274537FE5} = {5067FD6B-8F03-4502-AB83-8A391D801B47} + {B3D736D4-EFEE-44AA-B199-1A1C803F1793} = {86704890-D6B8-4166-853B-F1424B8396C7} + {8C89E1D0-2617-4B89-BA9C-9189FC69BC43} = {86704890-D6B8-4166-853B-F1424B8396C7} EndGlobalSection EndGlobal