diff --git a/03/MullItOver.Tests/InstructionExtensionsTests.cs b/03/MullItOver.Tests/InstructionExtensionsTests.cs new file mode 100644 index 0000000..af42709 --- /dev/null +++ b/03/MullItOver.Tests/InstructionExtensionsTests.cs @@ -0,0 +1,38 @@ +namespace MullItOver.Tests; + +public class InstructionExtensionsTests +{ + [Test] + public async Task Execute_WhenCalledWithNoConditionals_ItShouldReturnCorrectSum() + { + var instructions = new List() + { + new MulInstruction(2, 4), + new MulInstruction(5, 5), + new MulInstruction(11, 8), + new MulInstruction(8, 5), + }; + + var result = instructions.Execute(); + + await Assert.That(result).IsEqualTo(161); + } + + [Test] + public async Task Execute_WhenCalledWithConditionals_ItShouldReturnCorrectSum() + { + var instructions = new List() + { + new MulInstruction(2, 4), + new DontInstruction(), + new MulInstruction(5, 5), + new MulInstruction(11, 8), + new DoInstruction(), + new MulInstruction(8, 5), + }; + + var result = instructions.Execute(); + + await Assert.That(result).IsEqualTo(48); + } +} \ No newline at end of file diff --git a/03/MullItOver.Tests/InstructionTests.cs b/03/MullItOver.Tests/InstructionTests.cs new file mode 100644 index 0000000..0ae1d8d --- /dev/null +++ b/03/MullItOver.Tests/InstructionTests.cs @@ -0,0 +1,12 @@ +namespace MullItOver.Tests; + +public class InstructionTests +{ + [Test] + public async Task Execute_WhenCalled_ItShouldReturnProductOfMultiplicands() + { + var result = new MulInstruction(2, 2).Execute(); + + await Assert.That(result).IsEquivalentTo(4); + } +} \ No newline at end of file diff --git a/03/MullItOver.Tests/PuzzleInputTests.cs b/03/MullItOver.Tests/PuzzleInputTests.cs new file mode 100644 index 0000000..0a947ff --- /dev/null +++ b/03/MullItOver.Tests/PuzzleInputTests.cs @@ -0,0 +1,30 @@ +namespace MullItOver.Tests; + +public class PuzzleInputTests +{ + private readonly PuzzleParser _parser = new(); + + private async Task GetPuzzleInput() + { + var inputPath = Path.Combine(AppContext.BaseDirectory, "INPUT.txt"); + return await File.ReadAllTextAsync(inputPath); + } + + [Test] + public async Task Solve_WhenGivenInput_ItShouldSolvePartOne() + { + var input = await GetPuzzleInput(); + var result = _parser.Parse(input).Execute(); + + await Assert.That(result).IsEqualTo(174960292); + } + + [Test] + public async Task Solve_WhenGivenInput_ItShouldSolvePartTwo() + { + var input = await GetPuzzleInput(); + var result = _parser.ParseWithConditionals(input).Execute(); + + await Assert.That(result).IsEqualTo(56275602); + } +} \ No newline at end of file diff --git a/03/MullItOver.Tests/PuzzleParserTests.cs b/03/MullItOver.Tests/PuzzleParserTests.cs index 60e7e2b..427908e 100644 --- a/03/MullItOver.Tests/PuzzleParserTests.cs +++ b/03/MullItOver.Tests/PuzzleParserTests.cs @@ -3,6 +3,8 @@ public class PuzzleParserTests { private readonly PuzzleParser _parser = new(); + private const string TestInput = "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))"; + private const string Part2TestInput = "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))"; [Test] public async Task Parse_WhenInputIsEmpty_ReturnsEmptyList() @@ -11,4 +13,42 @@ public class PuzzleParserTests await Assert.That(result).IsEquivalentTo(new List()); } + + [Test] + public async Task Parse_WhenGivenSetOfMalformedInstructions_ItShouldReturnListOfActualInstructions() + { + var result = _parser.Parse(TestInput); + + await Assert.That(result).IsEquivalentTo(new List() + { + new MulInstruction(2, 4), + new MulInstruction(5, 5), + new MulInstruction(11, 8), + new MulInstruction(8, 5), + }); + } + + [Test] + public async Task ParseWithConditionals_WhenInputIsEmpty_ReturnsEmptyList() + { + var result = _parser.ParseWithConditionals(""); + + await Assert.That(result).IsEquivalentTo(new List()); + } + + [Test] + public async Task ParseWithConditionals_WhenGivenSetOfMalformedInstructionsItShouldReturnListOfActualInstructions() + { + var result = _parser.ParseWithConditionals(Part2TestInput); + + await Assert.That(result).IsEquivalentTo(new List() + { + new MulInstruction(2, 4), + new DontInstruction(), + new MulInstruction(5, 5), + new MulInstruction(11, 8), + new DoInstruction(), + new MulInstruction(8, 5), + }); + } } \ No newline at end of file diff --git a/03/MullItOver/Instruction.cs b/03/MullItOver/Instruction.cs new file mode 100644 index 0000000..ab78704 --- /dev/null +++ b/03/MullItOver/Instruction.cs @@ -0,0 +1,12 @@ +namespace MullItOver; + +public abstract class Instruction {}; + +class MulInstruction(int multiplicandOne, int multiplicandTwo) : Instruction +{ + public int Execute() => multiplicandOne * multiplicandTwo; +} + +class DontInstruction : Instruction; + +class DoInstruction : Instruction; \ No newline at end of file diff --git a/03/MullItOver/InstructionExtensions.cs b/03/MullItOver/InstructionExtensions.cs new file mode 100644 index 0000000..fe6b0fb --- /dev/null +++ b/03/MullItOver/InstructionExtensions.cs @@ -0,0 +1,28 @@ +namespace MullItOver; + +public static class InstructionExtensions +{ + public static int Execute(this List instructions) + { + var isEnabled = true; + var sum = 0; + + foreach (var instruction in instructions) + { + switch (instruction) + { + case DontInstruction: + isEnabled = false; + continue; + case DoInstruction: + isEnabled = true; + continue; + case MulInstruction mul when isEnabled: + sum += mul.Execute(); + continue; + } + } + + return sum; + } +} \ No newline at end of file diff --git a/03/MullItOver/Program.cs b/03/MullItOver/Program.cs index 1c97516..21e0b88 100644 --- a/03/MullItOver/Program.cs +++ b/03/MullItOver/Program.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using MullItOver; + if (args.Length is 0) { Console.WriteLine("Please provide a path to the input file."); @@ -18,21 +20,13 @@ var input = await File.ReadAllTextAsync(args[0]); var stopwatch = new Stopwatch(); stopwatch.Start(); -var instructions = new PuzzleParser().Parse(input); +var parser = new PuzzleParser(); -var results = 0; +var instructions = isPart2 + ? parser.ParseWithConditionals(input) + : parser.Parse(input); + +var results = instructions.Execute(); stopwatch.Stop(); -Console.WriteLine($"The sum of instructions is {results}. ({stopwatch.ElapsedMilliseconds}ms)"); - -class PuzzleParser -{ - public List Parse(string input) - { - return []; - } -} - -class Instruction(int multiplicandOne, int multiplicandTwo) -{ -} \ No newline at end of file +Console.WriteLine($"The sum of instructions is {results}. ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file diff --git a/03/MullItOver/PuzzleParser.cs b/03/MullItOver/PuzzleParser.cs new file mode 100644 index 0000000..535be46 --- /dev/null +++ b/03/MullItOver/PuzzleParser.cs @@ -0,0 +1,55 @@ +using System.Text.RegularExpressions; + +namespace MullItOver; + +partial class PuzzleParser +{ + [GeneratedRegex(@"mul\((\d{1,3}),(\d{1,3})\)")] + private static partial Regex InstructionsRegex(); + + [GeneratedRegex(@"mul\((\d{1,3}),(\d{1,3})\)|do\(\)|don't\(\)")] + private static partial Regex InstructionsWithConditionalsRegex(); + + public List Parse(string input) + { + var matches = InstructionsRegex().Matches(input); + return matches + .Select(Instruction (m) => new MulInstruction( + int.Parse(m.Groups[1].Value), + int.Parse(m.Groups[2].Value) + )) + .ToList(); + } + + public List ParseWithConditionals(string input) + { + var instructions = new List(); + var matches = InstructionsWithConditionalsRegex() + .Matches(input) + .ToList(); + + foreach (var match in matches) + { + var firstGroup = match.Groups[0].Value; + + if (firstGroup.Contains("mul")) + { + instructions.Add(new MulInstruction( + int.Parse(match.Groups[1].Value), + int.Parse(match.Groups[2].Value) + )); + continue; + } + + if (firstGroup.Contains('\'')) + { + instructions.Add(new DontInstruction()); + continue; + } + + instructions.Add(new DoInstruction()); + } + + return instructions; + } +} \ No newline at end of file diff --git a/README.md b/README.md index fc73a47..42ce693 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,4 @@ dotnet build |-----|----------------------------|:-----------------------------------:|----------------------------------------------------------------------------------------------| | 01 | [Problem](./01/PROBLEM.md) | [Solution](./01/HistorianHysteria/) | A great way to start! | | 02 | [Problem](./02/PROBLEM.md) | [Solution](./02/RedNosedReports/) | Damn their was an edge case that really bit me...direction change after first pair of levels | +| 03 | [Problem](./03/PROBLEM.md) | [Solution](./03/MullItOver/) | Thank goodness this wasn't a repeat of day 2. |