diff --git a/05/PrintQueue.Tests/PuzzleParserTests.cs b/05/PrintQueue.Tests/PuzzleParserTests.cs new file mode 100644 index 0000000..6463f0a --- /dev/null +++ b/05/PrintQueue.Tests/PuzzleParserTests.cs @@ -0,0 +1,76 @@ +namespace PrintQueue.Tests; + +public class PuzzleParserTests +{ + private readonly PuzzleParser _puzzleParser = new(); + private readonly string _exampleInput = $"47|53{Environment.NewLine}97|13{Environment.NewLine}97|61{Environment.NewLine}97|47{Environment.NewLine}75|29{Environment.NewLine}61|13{Environment.NewLine}75|53{Environment.NewLine}29|13{Environment.NewLine}97|29{Environment.NewLine}53|29{Environment.NewLine}61|53{Environment.NewLine}97|53{Environment.NewLine}61|29{Environment.NewLine}47|13{Environment.NewLine}75|47{Environment.NewLine}97|75{Environment.NewLine}47|61{Environment.NewLine}75|61{Environment.NewLine}47|29{Environment.NewLine}75|13{Environment.NewLine}53|13{Environment.NewLine}{Environment.NewLine}75,47,61,53,29{Environment.NewLine}97,61,53,29,13{Environment.NewLine}75,29,13{Environment.NewLine}75,97,47,61,53{Environment.NewLine}61,13,29{Environment.NewLine}97,13,75,29,47"; + + private static async Task GetPuzzleInput() + { + return await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt")); + } + + [Test] + public async Task Parse_WhenGivenExampleInput_ItShouldReturnExpectedRules() + { + var result = _puzzleParser.Parse(_exampleInput); + + await Assert.That(result.Rules).IsEquivalentTo(new List() + { + new(47, 53), + new(97, 13), + new(97, 61), + new(97, 47), + new(75, 29), + new(61, 13), + new(75, 53), + new(29, 13), + new(97, 29), + new(53, 29), + new(61, 53), + new(97, 53), + new(61, 29), + new(47, 13), + new(75, 47), + new(97, 75), + new(47, 61), + new(75, 61), + new(47, 29), + new(75, 13), + new(53, 13), + }); + } + + [Test] + public async Task Parse_WhenGivenExampleInput_ItShouldReturnExpectedUpdates() + { + var result = _puzzleParser.Parse(_exampleInput); + + await Assert.That(result.Updates).IsEquivalentTo(new List() + { + new([75,47,61,53,29]), + new([97,61,53,29,13]), + new([75,29,13]), + new([75,97,47,61,53]), + new([61,13,29]), + new([97,13,75,29,47]), + }); + } + + [Test] + public async Task Parse_WhenGivenPuzzleInput_ItShouldReturnExpectedResult() + { + var input = await GetPuzzleInput(); + + var parseResult = _puzzleParser.Parse(input); + + var validator = new UpdateValidator(parseResult.Rules); + + var result = parseResult.Updates + .Where(u => validator.Validate(u)) + .Select(u => u.GetMiddlePage()) + .Sum(); + + await Assert.That(result).IsEqualTo(6260); + } +} \ No newline at end of file diff --git a/05/PrintQueue.Tests/UpdateTests.cs b/05/PrintQueue.Tests/UpdateTests.cs new file mode 100644 index 0000000..4479f6c --- /dev/null +++ b/05/PrintQueue.Tests/UpdateTests.cs @@ -0,0 +1,19 @@ +namespace PrintQueue.Tests; + +public class UpdateTests +{ + [Test] + public async Task GetMiddlePage_WhenCalled_ItShouldReturnExpectedPage() + { + var updates = new List() + { + new([75,47,61,53,29]), + new([97,61,53,29,13]), + new([75,29,13]), + }; + + var result = updates.Select(u => u.GetMiddlePage()).Sum(); + + await Assert.That(result).IsEqualTo(143); + } +} \ No newline at end of file diff --git a/05/PrintQueue.Tests/UpdateValidatorTests.cs b/05/PrintQueue.Tests/UpdateValidatorTests.cs new file mode 100644 index 0000000..fff7d18 --- /dev/null +++ b/05/PrintQueue.Tests/UpdateValidatorTests.cs @@ -0,0 +1,84 @@ +namespace PrintQueue.Tests; + +public class UpdateValidatorTests +{ + private readonly List _exampleRules = [ + new(47, 53), + new(97, 13), + new(97, 61), + new(97, 47), + new(75, 29), + new(61, 13), + new(75, 53), + new(29, 13), + new(97, 29), + new(53, 29), + new(61, 53), + new(97, 53), + new(61, 29), + new(47, 13), + new(75, 47), + new(97, 75), + new(47, 61), + new(75, 61), + new(47, 29), + new(75, 13), + new(53, 13), + ]; + + [Test] + public async Task Graph_WhenGivenExampleRules_ItShouldBuildExpectedGraph() + { + var expectedGraph = new Dictionary>() + { + { 47, [53, 13, 61, 29] }, + { 53, [29, 13] }, + { 97, [13, 61, 47, 29, 53, 75] }, + { 13, [] }, + { 61, [13, 53, 29] }, + { 75, [29, 53, 47, 61, 13] }, + { 29, [13] }, + }; + + var validator = new UpdateValidator(_exampleRules); + + await Assert.That(validator.Graph.Keys).IsEquivalentTo(expectedGraph.Keys); + await Assert.That(validator.Graph.Values).IsEquivalentTo(expectedGraph.Values); + } + + [Test] + public async Task Validate_WhenGivenValidUpdate_ItShouldReturnTrue() + { + var updateOne = new Update([75, 47, 61, 53, 29]); + var updateTwo = new Update([97,61,53,29,13]); + var updateThree = new Update([75,29,13]); + + var validator = new UpdateValidator(_exampleRules); + + var resultOne = validator.Validate(updateOne); + var resultTwo = validator.Validate(updateTwo); + var resultThree = validator.Validate(updateThree); + + await Assert.That(resultOne).IsTrue(); + await Assert.That(resultTwo).IsTrue(); + await Assert.That(resultThree).IsTrue(); + } + + [Test] + public async Task Validate_WhenGivenInvalidUpdate_ItShouldReturnFalse() + { + var updateOne = new Update([75,97,47,61,53]); + var updateTwo = new Update([61,13,29]); + var updateThree = new Update([97,13,75,29,47]); + + var validator = new UpdateValidator(_exampleRules); + + var resultOne = validator.Validate(updateOne); + var resultTwo = validator.Validate(updateTwo); + var resultThree = validator.Validate(updateThree); + + await Assert.That(resultOne).IsFalse(); + await Assert.That(resultTwo).IsFalse(); + await Assert.That(resultThree).IsFalse(); + } +} \ No newline at end of file diff --git a/05/PrintQueue/OrderRule.cs b/05/PrintQueue/OrderRule.cs new file mode 100644 index 0000000..4487ab2 --- /dev/null +++ b/05/PrintQueue/OrderRule.cs @@ -0,0 +1,3 @@ +namespace PrintQueue; + +record OrderRule(int X, int Y); \ No newline at end of file diff --git a/05/PrintQueue/ParseResult.cs b/05/PrintQueue/ParseResult.cs new file mode 100644 index 0000000..29d4878 --- /dev/null +++ b/05/PrintQueue/ParseResult.cs @@ -0,0 +1,3 @@ +namespace PrintQueue; + +record ParseResult(List Rules, List Updates); \ No newline at end of file diff --git a/05/PrintQueue/Program.cs b/05/PrintQueue/Program.cs index 79274ad..a72944e 100644 --- a/05/PrintQueue/Program.cs +++ b/05/PrintQueue/Program.cs @@ -1,5 +1,7 @@ using System.Diagnostics; +using PrintQueue; + if (args.Length is 0) { Console.WriteLine("Please provide a path to the input file."); @@ -13,14 +15,21 @@ if (File.Exists(args[0]) is false) } var isPart2 = args.Length is 2 && args[1] is "part2"; -var input = await File.ReadAllLinesAsync(args[0]); +var input = await File.ReadAllTextAsync(args[0]); var stopwatch = new Stopwatch(); stopwatch.Start(); -// TODO: Perform work here - -stopwatch.Stop(); +var parser = new PuzzleParser(); -// TODO: Print results -Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file +var parseResult = parser.Parse(input); + +var validator = new UpdateValidator(parseResult.Rules); + +var result = parseResult.Updates + .Where(u => validator.Validate(u)) + .Select(u => u.GetMiddlePage()) + .Sum(); + +stopwatch.Stop(); +Console.WriteLine($"The sum of the page numbers is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file diff --git a/05/PrintQueue/PuzzleParser.cs b/05/PrintQueue/PuzzleParser.cs new file mode 100644 index 0000000..6902a97 --- /dev/null +++ b/05/PrintQueue/PuzzleParser.cs @@ -0,0 +1,36 @@ +namespace PrintQueue; + +class PuzzleParser +{ + public ParseResult Parse(string input) + { + var inputParts = input.Split($"{Environment.NewLine}{Environment.NewLine}"); + var rules = ParseRules(inputParts[0]); + var updates = ParseUpdates(inputParts[1]); + return new ParseResult(rules, updates); + } + + private static List ParseUpdates(string input) + { + return input.Split(Environment.NewLine) + .Select(s => new Update( + s.Split(",").Select(int.Parse).ToList() + )) + .ToList(); + } + + private static List ParseRules(string input) + { + return input + .Split(Environment.NewLine) + .Select(s => + { + var parts = s.Split("|"); + return new OrderRule( + int.Parse(parts[0]), + int.Parse(parts[1]) + ); + }) + .ToList(); + } +} \ No newline at end of file diff --git a/05/PrintQueue/Update.cs b/05/PrintQueue/Update.cs new file mode 100644 index 0000000..2f97f4b --- /dev/null +++ b/05/PrintQueue/Update.cs @@ -0,0 +1,9 @@ +namespace PrintQueue; + +record Update(List Pages) +{ + public int GetMiddlePage() + { + return Pages.Count is 0 ? 0 : Pages[Pages.Count / 2]; + } +} \ No newline at end of file diff --git a/05/PrintQueue/UpdateValidator.cs b/05/PrintQueue/UpdateValidator.cs new file mode 100644 index 0000000..e1f6c18 --- /dev/null +++ b/05/PrintQueue/UpdateValidator.cs @@ -0,0 +1,47 @@ +namespace PrintQueue; + +class UpdateValidator +{ + public readonly Dictionary> Graph = []; + + public UpdateValidator(List rules) + { + foreach (var rule in rules) + { + if (Graph.ContainsKey(rule.X) is false) + { + Graph.Add(rule.X, []); + } + + if (Graph.ContainsKey(rule.Y) is false) + { + Graph.Add(rule.Y, []); + } + + Graph[rule.X].Add(rule.Y); + } + } + + public bool Validate(Update update) + { + var previousPages = new List(); + + foreach (var page in update.Pages) + { + foreach (var previousPage in previousPages) + { + if ( + Graph.TryGetValue(previousPage, out HashSet? value) && + value.Contains(page) is false + ) + { + return false; + } + } + + previousPages.Add(page); + } + + return true; + } +} \ No newline at end of file