333 lines
15 KiB
C#
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
|
|
} |