diff --git a/README.md b/README.md index 3e0c351..633fe3e 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,7 @@ * [Day 6](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day6.cs) * [Day 7](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day7.cs) * [Day 8](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day8.cs) -* [Day 9](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day9.cs) \ No newline at end of file +* [Day 9](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day9.cs) +* [Day 10](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day10.cs) +* [Day 11](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day11.cs) +* [Day 12](https://github.com/TiagoRG/AdventOfCode/tree/main/AdventOfCode/Year2022/Day12.cs) \ No newline at end of file diff --git a/Year2022/Day12.cs b/Year2022/Day12.cs new file mode 100644 index 0000000..edc7e6e --- /dev/null +++ b/Year2022/Day12.cs @@ -0,0 +1,193 @@ +namespace AdventOfCode.Year2022; + +public class Day12 +{ + private const int SOrd = -14; + private const int EOrd = -28; + private static short[,]? _map; + private static (int, int) _sCoords; + private static (int, int) _eCoords; + + /// + /// Slowest solution so far. + /// Could most certainly use some improvements :skull: + /// + public Day12() + { + Console.WriteLine($@" +Day12 Solution +Part1 Result: {Part1()} +Part2 Result: {Part2()} + +============================="); + } + + private static void PartInit() + { + var lines = File.ReadAllLines("inputs/day12.txt"); + var map = new short[lines.Length, lines[0].Length]; + + for (var i = 0; i < lines.Length; i++) + for (var j = 0; j < lines[i].Length; j++) + map[i, j] = (short)(Convert.ToInt16(lines[i][j]) - 97); + + _map = map; + _sCoords = FindCoords(map, SOrd); + _eCoords = FindCoords(map, EOrd); + } + + private static int Part1() + { + PartInit(); + var distances = GetIndexes(_map!).Where(item => item != _sCoords) + .ToDictionary(index => (index.Item1, index.Item2), _ => short.MaxValue); + distances[(_sCoords.Item1, _sCoords.Item2)] = 0; + + _map![_sCoords.Item1, _sCoords.Item2] = 0; + _map[_eCoords.Item1, _eCoords.Item2] = (short)(Convert.ToInt16('z') - 97); + + var visited = new List<(int, int)>(); + var current = _sCoords; + var unvisited = GetIndexes(_map).ToList(); + + while (true) + try + { + Dijkstra(current, distances, visited, Part.Part1); + unvisited.Remove(current); + current = distances.Where(pair => unvisited.Contains(pair.Key)).MinBy(pair => pair.Value).Key; + } + catch (InvalidOperationException) + { + return distances[_eCoords]; + } + } + + private static int Part2() + { + PartInit(); + var distances = GetIndexes(_map!).ToDictionary(index => (index.Item1, index.Item2), _ => short.MaxValue); + distances[(_eCoords.Item1, _eCoords.Item2)] = 0; + + _map![_sCoords.Item1, _sCoords.Item2] = 0; + _map[_eCoords.Item1, _eCoords.Item2] = (short)(Convert.ToInt16('z') - 97); + + var visited = new List<(int, int)>(); + var current = _eCoords; + var unvisited = GetIndexes(_map).ToList(); + var aIndexes = FindAllCoords(_map, 0).ToList(); + + while (true) + { + if (aIndexes.Contains(Dijkstra(current, distances, visited, Part.Part2))) + return distances.Where(pair => aIndexes.Contains(pair.Key)).Min(pair => pair.Value); + unvisited.Remove(current); + current = distances.Where(pair => unvisited.Contains(pair.Key)).MinBy(pair => pair.Value).Key; + } + } + + private static (int, int) Dijkstra((int, int) current, IDictionary<(int, int), short> distances, + ICollection<(int, int)> visited, Part part) + { + var nextCoords = new Dictionary + { + ['D'] = (current.Item1 + 1, current.Item2), + ['U'] = (current.Item1 - 1, current.Item2), + ['R'] = (current.Item1, current.Item2 + 1), + ['L'] = (current.Item1, current.Item2 - 1) + }; + + if (current.Item1 < _map!.GetLength(0) - 1 + && IsStepPossible(current, nextCoords['D'], part) + && !visited.Contains(nextCoords['D'])) + UpdateDistances(distances, current, nextCoords['D']); + + if (current.Item1 > 0 + && IsStepPossible(current, nextCoords['U'], part) + && !visited.Contains(nextCoords['U'])) + UpdateDistances(distances, current, nextCoords['U']); + + if (current.Item2 < _map.GetLength(1) - 1 + && IsStepPossible(current, nextCoords['R'], part) + && !visited.Contains(nextCoords['R'])) + UpdateDistances(distances, current, nextCoords['R']); + + if (current.Item2 > 0 + && IsStepPossible(current, nextCoords['L'], part) + && !visited.Contains(nextCoords['L'])) + UpdateDistances(distances, current, nextCoords['L']); + + visited.Add(current); + return current; + } + + private static bool IsStepPossible((int, int) currentCoords, (int, int) nextCoord, Part part) + { + return part switch + { + Part.Part1 => _map![nextCoord.Item1, nextCoord.Item2] - 1 <= _map[currentCoords.Item1, currentCoords.Item2], + Part.Part2 => _map![nextCoord.Item1, nextCoord.Item2] + 1 >= _map[currentCoords.Item1, currentCoords.Item2], + _ => throw new ArgumentOutOfRangeException(nameof(part), part, null) + }; + } + + private static void UpdateDistances(IDictionary<(int, int), short> distances, (int, int) currentCoords, + (int, int) nextCoords) + { + if (distances[(nextCoords.Item1, nextCoords.Item2)] == short.MaxValue || + distances[(nextCoords.Item1, nextCoords.Item2)] > distances[(currentCoords.Item1, currentCoords.Item2)]) + distances[(nextCoords.Item1, nextCoords.Item2)] = + (short)(distances[(currentCoords.Item1, currentCoords.Item2)] + 1); + } + + /// + /// Finds the first occurrence of a value in a 2D array + /// + /// The 2D array + /// The value to be found + /// + /// Array can't be null + private static (int, int) FindCoords(short[,] array, int value) + { + if (array == null) throw new ArgumentNullException(nameof(array)); + foreach (var index in GetIndexes(array)) + if (array[index.Item1, index.Item2] == value) + return (index.Item1, index.Item2); + return (-1, -1); + } + + /// + /// Finds all coordinates of a value in a 2D array + /// + /// The 2D array + /// The value to be found + /// + /// Array can't be null + private static IEnumerable<(int, int)> FindAllCoords(short[,] array, int value) + { + if (array == null) throw new ArgumentNullException(nameof(array)); + return from index in GetIndexes(array) + where array[index.Item1, index.Item2] == value + select (index.Item1, index.Item2); + } + + /// + /// Returns an enumerable of all indexes of a 2D array + /// + /// The 2D array + /// + /// Array can't be null + private static IEnumerable<(int, int)> GetIndexes(short[,] array) + { + if (array == null) throw new ArgumentNullException(nameof(array)); + for (var i = 0; i < array.GetLength(0); i++) + for (var j = 0; j < array.GetLength(1); j++) + yield return (i, j); + } + + private enum Part + { + Part1, + Part2 + } +} \ No newline at end of file