Files
AdventOfCode/Days/Day12.cs

333 lines
15 KiB
C#

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(bool display = true)
{
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);
}
if (display)
{
AnsiConsole.Write(canvas);
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine($"[green]Minimum path length: [yellow]{minimumPathLength.length}[/][/]");
}
}
public override void RunPart2(bool display = true)
{
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);
}
if (display)
{
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
}