diff --git a/16/ReindeerMaze.Tests/PuzzleSolverTests.cs b/16/ReindeerMaze.Tests/PuzzleSolverTests.cs new file mode 100644 index 0000000..546a3f5 --- /dev/null +++ b/16/ReindeerMaze.Tests/PuzzleSolverTests.cs @@ -0,0 +1,62 @@ +namespace ReindeerMaze.Tests; + +public class PuzzleSolverTests +{ + [Test] + [MethodDataSource(nameof(SolveTestCases))] + public async Task Solve_WhenCalledWithInput_ItShouldReturnExpectedValue(SolveTestCase testCase) + { + var result = PuzzleSolver.Solve(testCase.Input); + + await Assert.That(result).IsEqualTo(testCase.ExpectedValue); + } + + public static IEnumerable> SolveTestCases() + { + yield return () => new( + [ + "###############", + "#.......#....E#", + "#.#.###.#.###.#", + "#.....#.#...#.#", + "#.###.#####.#.#", + "#.#.#.......#.#", + "#.#.#####.###.#", + "#...........#.#", + "###.#.#####.#.#", + "#...#.....#.#.#", + "#.#.#.###.#.#.#", + "#.....#...#.#.#", + "#.###.#.#.#.#.#", + "#S..#.....#...#", + "###############", + ], + 7036 + ); + + yield return () => new( + [ + "#################", + "#...#...#...#..E#", + "#.#.#.#.#.#.#.#.#", + "#.#.#.#...#...#.#", + "#.#.#.#.###.#.#.#", + "#...#.#.#.....#.#", + "#.#.#.#.#.#####.#", + "#.#...#.#.#.....#", + "#.#.#####.#.###.#", + "#.#.#.......#...#", + "#.#.###.#####.###", + "#.#.#...#.....#.#", + "#.#.#.#####.###.#", + "#.#.#.........#.#", + "#.#.#.#########.#", + "#S#.............#", + "#################", + ], + 11048 + ); + } + + public record SolveTestCase(string[] Input, int ExpectedValue); +} \ No newline at end of file diff --git a/16/ReindeerMaze/Program.cs b/16/ReindeerMaze/Program.cs index 5f94bf8..4bc4da6 100644 --- a/16/ReindeerMaze/Program.cs +++ b/16/ReindeerMaze/Program.cs @@ -1,24 +1,107 @@ using System.Diagnostics; -if (args.Length is 0) -{ - Console.WriteLine("Please provide a path to the input file."); - return; -} - -if (File.Exists(args[0]) is false) -{ - Console.WriteLine("The provided file does not exist."); - return; -} +// if (args.Length is 0) +// { +// Console.WriteLine("Please provide a path to the input file."); +// return; +// } +// +// if (File.Exists(args[0]) is false) +// { +// Console.WriteLine("The provided file does not exist."); +// return; +// } var isPart2 = args.Length is 2 && args[1] is "part2"; -var input = await File.ReadAllLinesAsync(args[0]); +var input = await File.ReadAllLinesAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt")); var stopwatch = new Stopwatch(); stopwatch.Start(); -// TODO: Implement solution +var result = PuzzleSolver.Solve(input); stopwatch.Stop(); -Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file +Console.WriteLine($"The lowest score a Reindeer could get is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); + +static class PuzzleSolver +{ + public static int Solve(string[] input) + { + List<(int Xd, int Yd)> directions = [ + (1, 0), + (0, 1), + (-1, 0), + (0, -1), + ]; + + var rows = input.Length; + var columns = input[0].Length; + + (int X, int Y) start = (-1, -1); + (int X, int Y) end = (-1, -1); + + for (var row = 0; row < rows; row++) + { + for (var column = 0; column < columns; column++) + { + var currentValue = input[row][column]; + + switch (currentValue) + { + case 'S': + start = (column, row); + break; + case 'E': + end = (column, row); + break; + } + } + } + + var queue = new PriorityQueue<(int X, int Y, int Direction, int Score), int>(); + var visited = new HashSet<(int X, int Y, int Direction)>(); + var initialScore = 0; + var initialDirection = 0; + + queue.Enqueue((start.X, start.Y, initialDirection, initialScore), initialScore); + + while (queue.Count is not 0) + { + var current = queue.Dequeue(); + + var possibleVisited = (current.X, current.Y, current.Direction); + + if (visited.Contains((possibleVisited))) + { + continue; + } + + visited.Add(possibleVisited); + + if (current.X == end.X && current.Y == end.Y) + { + return current.Score; + } + + var currentDirection = directions[current.Direction]; + var nextX = current.X + currentDirection.Xd; + var nextY = current.Y + currentDirection.Yd; + + if (nextX >= 0 && nextX < columns && nextY >= 0 && nextY < rows && input[nextY][nextX] != '#') + { + var newScoreAfterMovingForward = current.Score + 1; + queue.Enqueue((nextX, nextY, current.Direction, newScoreAfterMovingForward), newScoreAfterMovingForward); + } + + var newScoreAfterTurning = current.Score + 1000; + + var directionAfterClockWiseTurn = (current.Direction + 1) % directions.Count; + queue.Enqueue((current.X, current.Y, directionAfterClockWiseTurn, newScoreAfterTurning), newScoreAfterTurning); + + var directionAfterCounterClockWiseTurn = (current.Direction + 3) % directions.Count; + queue.Enqueue((current.X, current.Y, directionAfterCounterClockWiseTurn, newScoreAfterTurning), newScoreAfterTurning); + } + + return 0; + } +} \ No newline at end of file