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 } }