using Spectre.Console; namespace AdventOfCode.Days; public class Day12 : Day { public override int Number => 12; public override string Name => "Hill Climbing Algorithm"; public const int EndElevation = 25; // Color map of elevation: blue -> green -> yellow -> orange -> red private readonly Color[] _colorMap = { Color.Blue1, Color.DodgerBlue1, Color.DeepSkyBlue1, Color.Turquoise2, Color.Cyan1, Color.Cyan2, Color.MediumSpringGreen, Color.SpringGreen1, Color.SpringGreen2_1, Color.Green1, Color.DarkOliveGreen2, Color.GreenYellow, Color.DarkOliveGreen1, Color.Yellow2, Color.Khaki1, Color.Yellow1, Color.NavajoWhite1, Color.LightGoldenrod2_1, Color.Gold1, Color.SandyBrown, Color.Orange1, Color.DarkOrange, Color.OrangeRed1, Color.IndianRed1, Color.HotPink, Color.MediumOrchid1_1 }; private (int x, int y) _end; private (int x, int y) _start; public override void RunPart1() { var map = ParseMap(); var canvas = DrawMap(map); // Test all possible solutions var minimumPathLength = ExplorePath(map); // Draw solution path foreach (var (x, y) in minimumPathLength.path) { canvas.SetPixel(x, y, Color.White); } AnsiConsole.Write(canvas); AnsiConsole.WriteLine(); AnsiConsole.MarkupLine($"[green]Minimum path length: [yellow]{minimumPathLength.length}[/][/]"); } public override void RunPart2() { var map = ParseMap(); var canvas = DrawMap(map); // Test all possible solutions var minimumPathLength = FindNearestStart(map); // Draw solution path foreach (var (x, y) in minimumPathLength.path) { canvas.SetPixel(x, y, Color.White); } AnsiConsole.Write(canvas); AnsiConsole.WriteLine(); AnsiConsole.MarkupLine($"[green]Minimum path length: [yellow]{minimumPathLength.length}[/][/]"); } private (int length, List<(int x, int y)> path) ExplorePath(byte[,] map) { var height = map.GetLength(0); var width = map.GetLength(1); // Store seen positions with number of steps to reach this position var exploredPositions = new Dictionary<(int x, int y), int>(); var toExplore = new Queue<(int x, int y, int elevation, int steps, List<(int x, int y)> path)>(); var pathLengths = new List<(int length, List<(int x, int y)> path)>(); toExplore.Enqueue((_start.x, _start.y, 0, 0, new())); // Explore all possible path while (toExplore.Count > 0) { var (x, y, previousElevation, steps, path) = toExplore.Dequeue(); // If we went out of bound if (x < 0 || x >= width || y < 0 || y >= height) { continue; } // If new elevation is too high var newElevation = map[y, x]; if (newElevation - previousElevation > 1) { continue; } // If new path is not better than old path, just skip if (exploredPositions.TryGetValue((x, y), out var oldSteps)) { if (oldSteps <= steps) { continue; } } // Mark this position as explored and store required number of steps to reach it exploredPositions[(x, y)] = steps; // Update path var newPath = new List<(int x, int y)>(path) { (x, y) }; // If we found the end, note that we could end the search here since it's a bfs, there can't be // any shorter path to this point if ((x, y) == _end) { pathLengths.Add((steps, newPath)); continue; } var nextPositions = new (int x, int y)[] { (x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1) }; foreach (var nextPosition in nextPositions) { toExplore.Enqueue((nextPosition.x, nextPosition.y, newElevation, steps + 1, newPath)); } } return pathLengths.MinBy(p => p.length); } private (int length, List<(int x, int y)> path) FindNearestStart(byte[,] map) { var height = map.GetLength(0); var width = map.GetLength(1); // Store seen positions with number of steps to reach this position var exploredPositions = new Dictionary<(int x, int y), int>(); var toExplore = new Queue<(int x, int y, int elevation, int steps, List<(int x, int y)> path)>(); var pathLengths = new List<(int length, List<(int x, int y)> path)>(); toExplore.Enqueue((_end.x, _end.y, EndElevation, 0, new())); // Explore all possible path while (toExplore.Count > 0) { var (x, y, previousElevation, steps, path) = toExplore.Dequeue(); // If we went out of bound if (x < 0 || x >= width || y < 0 || y >= height) { continue; } // If new elevation is too low, skip var newElevation = map[y, x]; if (previousElevation - newElevation > 1) { continue; } // If new path is not better than old path, just skip if (exploredPositions.TryGetValue((x, y), out var oldSteps)) { if (oldSteps <= steps) { continue; } } // Mark this position as explored and store required number of steps to reach it exploredPositions[(x, y)] = steps; // Update path var newPath = new List<(int x, int y)>(path) { (x, y) }; // If we found a possible start, same as in the 1st part, we could stop here if (newElevation == 0) { pathLengths.Add((steps, newPath)); continue; } var nextPositions = new (int x, int y)[] { (x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1) }; foreach (var nextPosition in nextPositions) { toExplore.Enqueue((nextPosition.x, nextPosition.y, newElevation, steps + 1, newPath)); } } return pathLengths.MinBy(p => p.length); } private byte[,] ParseMap() { var lines = Input.ReadAllLines().ToArray(); var lineCount = lines.Length; var columnCount = lines[0].Length; var map = new byte[lineCount, columnCount]; for (int i = 0; i < lineCount; i++) { var line = lines[i]; for (int j = 0; j < columnCount; j++) { var elevation = line[j]; map[i, j] = elevation switch { 'S' => 0, 'E' => EndElevation, _ => (byte) (elevation - 'a') }; // Store the end if (elevation == 'E') { _end = (j, i); } // Store the start if (elevation == 'S') { _start = (j, i); } } } return map; } private Canvas DrawMap(byte[,] map) { var height = map.GetLength(0); var width = map.GetLength(1); var canvas = new Canvas(width, height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { canvas.SetPixel(x, y, _colorMap[map[y, x]]); } } return canvas; } #region Input public const string Input = """ abccccccaaccaaccccaaaaacccccaaaaccccccccccccccccccccccccccccccccaaaaaaaaaaaaaaaaaaaccccccccccccccccaaaccccccccccccaacccccccccccccccccccccccccccccccccccccccccaaaa abaaaaccaaaaaccccaaaaaccccccaaaaccccccccccccccccccccaaacccccccccccaaaaaaaaaaaaaaaaaaccccccccccccccccaaaaccccccaaacaaccccccccccccccccccccccccccccccccccccccccaaaaa abaaacccaaaaaaaacaaaaaacccccaaaaccccccccccccccccccccaaaaacccccccccaaaaaaaaaaaaaaaaacccccccccaaccccaaaaaacccccccaaaaaccccaaccccccccccccccacccccccccccccccccccaaaaa abaaacccccaaaaaccccaaaaccccccaaacccccccccccccccccccaaaaaccccccccccaaaaaacacaaaaaacccccccccccaaccccaaaaacccccccccaaaaaaccaaaaaaccccccccccaaaccccacccccccccccaaaaaa abaacccccaaaaaccccaacccccccccccccccaaaaacccccccccccaaaaacccccccccaaaaaaaaccaaaaaaacccccccaaaaaaaaccaaaaacccccccaaaaaaaccaaaaacccccccccccaaacccaaaccccccccccccccaa abaaacccaaacaacccccccccccccccccccccaaaaaccccccccccccaaaaacccccccaaaaaaaaaccaaccaaacccccccaaaaaaaaccaacccccccccaaaaaaccaaaaaaccccccccccccaaaacaaaaccccccccccccccaa abaaacccccccaaccccccccccccccccccccaaaaaaccccccccccccaaccccccaacccaaaccaaaaccccccaacccccccccaaaacccccccccccccccaacaaaccaaaaaaaccccccccccccajjjjjjjcccccccccccccccc abcaacccccccccccccccccccccccccccccaaaaaaccccccccccccccccccccaaaaccccccaaaaccccccccccccccaacaaaaaccccccccccccccccccaaccccaaaaaacccccccccccjjjjjjjjjcccccaaaccccccc abccccccccccccccccccccccccccccccccaaaaaaccaaccccccccccccccaaaaaacccccccaaacccccccccccaacaaaaaaaaccccccccccccccccccccccccaaccaaccccccccaiijjjjojjjjcccccaaacaccccc abcccccccccccccccccccccccaaacccccccaaacacaaacccccccccccccccaaaaccccaaccccccccccccccccaaaaaaacccaccccccccccccccccccccccccaacccccccccccaiiijjooooojjkccaaaaaaaacccc abccccccccccccccccccccccaaaaccccccccccaaaaaccccccccccccccccaaaaacccaaaaaccccccccccccccaaaaaacccccccccccccccccccccccccccccccccccccciiiiiiiioooooookkkcaaaaaaaacccc abccccccccccccccccccccccaaaaccccccccccaaaaaaaacccccccccccccaacaaccaaaaacccccccaaacccaaaaaaaaccccccccccccccccccccccccccccccccccchiiiiiiiiooooouoookkkccaaaaaaccccc abcccccccccaaccccccccccccaaaccccccccccccaaaaacccccccccccccccccccccaaaaaccccccaaaacccaaaaacaacccccccccccccaacaacccccccccccccccchhhiiiinnnooouuuuoookkkccaaaaaccccc abcccccccccaaacccccccccccccccccccccccccaaaaacccccccccccccccccccccccaaaaacccccaaaaccccccaaccccccccccccccccaaaaacccccccccccccccchhhnnnnnnnnouuuuuuppkkkkaaaaaaccccc abccccccaaaaaaaacccaaccccccccccccccccccaacaaccaacaaccccccccccccccccaacccccccccaaaccccccaacccccccccccccccaaaaacccccccccccccccchhhnnnnnnnnntuuxuuupppkkkkkacccccccc abccccccaaaaaaaacacaaaacccccccccccccccccccaaccaaaaacccccccccccccccccccccccccccccccccccccccccccccccccccccaaaaaacccccccaacccccchhhnnnnttttttuxxxuuppppkkkkkcccccccc abcccccccaaaaaaccaaaaaaccccccccccaaccccccccccaaaaaccccccccccccccccccccccccaaacccccccccccccccccccccccccccccaaaaccaaccaaacccaaahhhnnntttttttuxxxxuupppppllllccccccc abcccccccaaaaaacccaaaacccccccccaaaaaaccccccccaaaaaacccccccccccccccccccccccaaacccccccccccccccccccccccccccccacccccaaaaaaacaaaaahhhppntttxxxxxxxxuuuuvpppplllccccccc abcccccccaaaaaacccaaaacccccccccaaaaaacccccaaaaaaaaaccccccccccccccccccccaaaaaaaacccccccccccccccccccccaaaccccccaacaaaaaaccaaaaahhhpppttxxxxxxxxyuuvvvvvppplllcccccc abcccccccaaccaacccaacaccaaaaccccaaaaacccccaaaaaaaaaccccccccccccccccccccaaaaaaaacccccccccccccccccccccaaacaaaaaaaccaaaaaaaaaaaaahhppptttxxxxxxyyyyyyvvvppplllcccccc SbccccccccccccccccccccccaaaacccaaaaacccccccaaaaaaaaacaaaccccccccaacccccccaaaaaccccccccaaaaacccccccccaaaaaaaaaaaaaaaaaaaaaaaaacgggpppttxxxxEzzyyyyyvvvqqqlllcccccc abccccccccccccccccccccccaaaacccaaaaacccccccaaaaaaaaccaaaccccccccaaacaaccaaaaaaccccccccaaaaacccccccaaaaaaaaaaaaaaaaaaaaaaaaaaacgggpppsssxxxyyyyyyvvvvvqqlllccccccc abcccaaaccccccccccccccccaaaccccccccccccccccaaaaaaaaaaaaaccccccccaaaaaaccaaaaaacccccccaaaaaacccaaaccaaaaaccaaaaaaaaaaaacccccccccgggppssswwyyyyyyvvvvqqqqlllccccccc abcaaaaaccccccccccccccccccccccccccccccccccaaaaaaaaaaaaacccccccaaaaaaacccaccaaacccccccaaaaaacccaaacccaaaaaaaaaaaccccaaacccaaaaacgggppsswwwyyyyyyvvqqqqqlllcccccccc abcaaaaaaccccccccccccccccccccccccccccccccccaaccaaaaaaaaaaaccccaaaaaaacccccccccccccccccaaaaacccaaacaaaacaaaaaaaaccccaaacccaaaaacggpppsswwwywwyyyvvqqqmmmlccccccccc abcaaaaaacccccccaacaaccccccccccccccccccccccccccaaaaaaaaaaaccccccaaaaacccccccccccccccccaaaccaaaaaaaaaaacccccccaacccccccccaaaaaacggpppsswwwwwwwwyvvqqqmmmcccccccccc abcaaaaaccccccccaaaaaccccccccccccccccccccccccccccaaaaaaaacccccccaacaaacccccccccccccccccccccaaaaaaaaaccccccccccccccccccccaaaaaagggoossswwwwrrwwwvvqqmmmccccccccccc abcaaaaacccccccaaaaaccccccccccccccccccccccccccccaaaaaaacccccccccaaccccccccccccccccccccccccccaaaaaaacccccccccccaaaccccccccaaaaagggooosssssrrrrwwwvqqmmmcccaacccccc abcccccccccccccaaaaaaccccccccccccccccccccaacccccccccaaaccccccccccccccccccccccccccccccccccccccaaaaaaccccccccccccaaaaccccccaaaccgggooosssssrrrrrwwrrqmmmcccaacccccc abcccccccccccccccaaaacccccccccccccccccccaaaacccccccacaaacccccccccccccccccccccccccccccccccccccaaaaaaacccccccccaaaaaacccccccccccgffoooooosoonrrrrrrrrmmmccaaaaacccc abcccccccccccccccaccccccccccccccccccccccaaaacccccccaaaaacccccccccccccccccccccccccccccccccccccaaacaaacccccccccaaaaacccccccccccccfffoooooooonnnrrrrrmmmddcaaaaacccc abccccccccccccccccccccccccccccccccccccccaaaaccccccccaaaaacccccccccccccccccccccccccaaaccccccccaacccccccccccccccaaaaaccccccccccccffffoooooonnnnnnrnnmmmdddaaaaacccc abcccccccccccccccccccccccccccccccccccccccccccccccccaaaaaacccccccccccccccccaaaaaccaaaacccccccccccccccccccccccccaacccccccccccccccfffffffffeeeennnnnnmmdddaaaacccccc abcccccccaaaccccccccaccccccccccccccccccccccccccccccaaaaccccccccccccaaaccccaaaaaccaaaaccccccccccccccccccccccccccccccccccccccccccccfffffffeeeeennnnnmddddaaaaaccccc abcccaaccaaacccccaaaacccccaacccccccccccccccccccccccccaaacccccccccccaaacccaaaaaacccaaaccccccccccccccccccccccccccccccccccccccccccccccffffeeeeeeeedddddddcccaacccccc abcccaaaaaaacccccaaaaaaccaaacccccccccccccccccccccccccccacccccccccccaaaaccaaaaaaccccccccccccccccccccccccccaacccccccccaaaccccccccccccccaaaaaaeeeeedddddcccccccccccc abcccaaaaaacccccccaaaacccaaacaaaccccaaaacccccccccaaacaaaccccccaacccaaaacaaaaaaacccccccccccccccccccccccccaaaccccccccaaaacccccccccccccccccccaaaaeeddddccccccccccccc abccccaaaaaaaacccaaaaaaaaaaaaaaaccccaaaacccccccccaaaaaaacccccaaacccaaaaaaaaaacccccccccccccccccccccccaaacaaaccccccccaaaaccccccccccccccccccccaaaccccccccccccccaaaca abcccaaaaaaaaacccaacaaaaaaaaaaacccccaaaaccccccccccaaaaaacaaacaaacaaaaaaaaaacccccccccccccccaaacccccccaaaaaaaaaaccccccaaaccccccccccccccccccccaacccccccccccccccaaaaa abcccaaaaaaaacccccccccccaaaaaacccccccaacccccccccccaaaaaaaaaaaaaaaaaaaaaaaaccccccccccccccccaaaacccccccaaaaaaaaacccccccccccccccccccccccccccccaaacccccccccccccccaaaa abccaaaaaaacccccccccccccaaaaaaacccccccccccccccccaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccaaaacccccccaaaaaaaacccccccccccccccccccccccccccccccccccccccccccccccaaaaa """; #endregion }