Initial Commit
This commit is contained in:
279
2022/Day12CSharp/Program.cs
Normal file
279
2022/Day12CSharp/Program.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
Part01();
|
||||
Part02();
|
||||
|
||||
void Part01()
|
||||
{
|
||||
Console.WriteLine("!! === Part 1 === !!");
|
||||
|
||||
Console.WriteLine(" === Example ===");
|
||||
Tuple<int, int> exampleStart, exampleEnd;
|
||||
var exampleHeightMap = HeightMap.LoadHeightMap("example-input.txt", out exampleStart, out exampleEnd);
|
||||
var exampleGraph = exampleHeightMap.ToGraph();
|
||||
exampleGraph.PrintShortestPath(exampleStart, exampleEnd);
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine(" === Puzzle ===");
|
||||
Tuple<int, int> puzzleStart, puzzleEnd;
|
||||
var puzzleHeightMap = HeightMap.LoadHeightMap("puzzle-input.txt", out puzzleStart, out puzzleEnd);
|
||||
var puzzleGraph = puzzleHeightMap.ToGraph();
|
||||
puzzleGraph.PrintShortestPath(puzzleStart, puzzleEnd);
|
||||
}
|
||||
|
||||
void Part02()
|
||||
{
|
||||
Console.WriteLine("!! === Part 2 === !!");
|
||||
|
||||
Console.WriteLine(" === Example ===");
|
||||
Tuple<int, int> exampleStart, exampleEnd;
|
||||
var exampleHeightMap = HeightMap.LoadHeightMap("example-input.txt", out exampleStart, out exampleEnd);
|
||||
var exampleGraph = exampleHeightMap.ToGraph();
|
||||
Console.WriteLine("Shortest steps from anywhere: {0}", exampleGraph.GetShortestNumberOfStepsAnywhere(0, exampleEnd));
|
||||
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine(" === Puzzle ===");
|
||||
Tuple<int, int> puzzleStart, puzzleEnd;
|
||||
var puzzleHeightMap = HeightMap.LoadHeightMap("puzzle-input.txt", out puzzleStart, out puzzleEnd);
|
||||
var puzzleGraph = puzzleHeightMap.ToGraph();
|
||||
Console.WriteLine("Shortest steps from anywhere: {0}", puzzleGraph.GetShortestNumberOfStepsAnywhere(0, puzzleEnd));
|
||||
|
||||
}
|
||||
|
||||
class GraphNode
|
||||
{
|
||||
public int Height { get; set; }
|
||||
public Tuple<int, int> Coordinates { get; set; }
|
||||
|
||||
public List<GraphNode> AdjacentNodes { get; set; } = new List<GraphNode>();
|
||||
|
||||
public bool Explored { get; set; }
|
||||
|
||||
public GraphNode(int height, Tuple<int, int> coordinates)
|
||||
{
|
||||
Height = height;
|
||||
Coordinates = coordinates;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Explored = false;
|
||||
}
|
||||
}
|
||||
|
||||
class Graph
|
||||
{
|
||||
public Dictionary<Tuple<int, int>, GraphNode> Vertices { get; set; } = new Dictionary<Tuple<int, int>, GraphNode>();
|
||||
|
||||
public GraphNode GetAt(Tuple<int, int> index)
|
||||
{
|
||||
return Vertices[index];
|
||||
}
|
||||
|
||||
public int GetShortestNumberOfStepsAnywhere(int startingHeight, Tuple<int, int> end)
|
||||
{
|
||||
int minSteps = int.MaxValue;
|
||||
|
||||
foreach (var g in Vertices)
|
||||
{
|
||||
if (g.Value.Height == startingHeight)
|
||||
{
|
||||
minSteps = Math.Min(minSteps, GetShortestNumberOfSteps(g.Value.Coordinates, end));
|
||||
}
|
||||
}
|
||||
|
||||
return minSteps;
|
||||
}
|
||||
|
||||
public GraphNode GetAt(int x, int y)
|
||||
{
|
||||
return GetAt(new Tuple<int, int>(x, y));
|
||||
}
|
||||
|
||||
public int GetShortestNumberOfSteps(Tuple<int, int> start, Tuple<int, int> end)
|
||||
{
|
||||
Dictionary<Tuple<int, int>, Tuple<GraphNode?, int>> path = new Dictionary<Tuple<int, int>, Tuple<GraphNode?, int>>();
|
||||
|
||||
BFS(start, end, path);
|
||||
|
||||
return path[end].Item2;
|
||||
}
|
||||
|
||||
public void PrintShortestPath(Tuple<int, int> start, Tuple<int, int> end)
|
||||
{
|
||||
Console.WriteLine("Shortest path from ({0},{1}) to ({2},{3}): {4} steps", start.Item1, start.Item2, end.Item1, end.Item2, GetShortestNumberOfSteps(start, end));
|
||||
}
|
||||
|
||||
public bool BFS(Tuple<int,int> start, Tuple<int,int> end, Dictionary<Tuple<int, int>, Tuple<GraphNode?, int>> path)
|
||||
{
|
||||
Queue<GraphNode> queue = new Queue<GraphNode>();
|
||||
int visited = 0;
|
||||
|
||||
// label all verts as unexplored
|
||||
foreach (var g in Vertices)
|
||||
{
|
||||
g.Value.Reset();
|
||||
path.Add(g.Value.Coordinates, new Tuple<GraphNode?, int>(null, int.MaxValue));
|
||||
}
|
||||
|
||||
// Get the root
|
||||
GraphNode root = GetAt(start);
|
||||
|
||||
Vertices[root.Coordinates].Explored = true;
|
||||
path[root.Coordinates] = new Tuple<GraphNode?,int>(path[root.Coordinates].Item1, 0);
|
||||
queue.Enqueue(root);
|
||||
|
||||
while (queue.Count != 0)
|
||||
{
|
||||
GraphNode v = queue.Dequeue();
|
||||
|
||||
// We done if we found it
|
||||
if (v.Coordinates.Equals(end))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var w in v.AdjacentNodes)
|
||||
{
|
||||
if (!w.Explored)
|
||||
{
|
||||
visited++;
|
||||
//Console.WriteLine("Visited: ({0},{1}). Count: {2}", w.Coordinates.Item1, w.Coordinates.Item2, visited);
|
||||
w.Explored = true;
|
||||
path[w.Coordinates] = new Tuple<GraphNode?, int>(v, path[v.Coordinates].Item2 + 1);
|
||||
queue.Enqueue(w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class HeightMap
|
||||
{
|
||||
public Graph ToGraph()
|
||||
{
|
||||
Graph graph = new Graph();
|
||||
|
||||
// Add in all the verts
|
||||
for (int j = 0; j < heightmap.Count; j++)
|
||||
{
|
||||
for (int i = 0; i < heightmap[0].Count; i++)
|
||||
{
|
||||
GraphNode gn = new GraphNode(GetHeight(i, j), new Tuple<int, int>(i, j));
|
||||
graph.Vertices.Add(gn.Coordinates, gn);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the edges
|
||||
for (int j = 0; j < heightmap.Count; j++)
|
||||
{
|
||||
for (int i = 0; i < heightmap[0].Count; i++)
|
||||
{
|
||||
Tuple<int, int> current = new Tuple<int,int>(i, j);
|
||||
Tuple<int, int> result = new Tuple<int, int>(0, 0);
|
||||
|
||||
char[] edges = { 'u', 'd', 'l', 'r' };
|
||||
|
||||
foreach (var edge in edges)
|
||||
{
|
||||
if (CanGo(edge, i, j, out result))
|
||||
{
|
||||
graph.Vertices[current].AdjacentNodes.Add(graph.Vertices[result]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
private HeightMap()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public int GetHeight(int x, int y)
|
||||
{
|
||||
return heightmap[y][x];
|
||||
}
|
||||
|
||||
public bool CanGo(char direction, int x, int y, out Tuple<int,int> newSpot)
|
||||
{
|
||||
int currentHeight = GetHeight(x, y);
|
||||
|
||||
switch (direction)
|
||||
{
|
||||
case 'u':
|
||||
newSpot = new Tuple<int, int>(x, y - 1);
|
||||
return (y > 0 && currentHeight >= GetHeight(x, y - 1) - 1);
|
||||
|
||||
case 'd':
|
||||
newSpot = new Tuple<int, int>(x, y + 1);
|
||||
return (y < heightmap.Count - 1 && currentHeight >= GetHeight(x, y + 1) - 1);
|
||||
|
||||
case 'l':
|
||||
newSpot = new Tuple<int, int>(x - 1, y);
|
||||
return (x > 0 && currentHeight >= GetHeight(x - 1, y) - 1);
|
||||
|
||||
case 'r':
|
||||
newSpot = new Tuple<int, int>(x + 1, y);
|
||||
return (x < heightmap[0].Count - 1 && currentHeight >= GetHeight(x + 1, y) - 1);
|
||||
default:
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
private List<List<int>> heightmap = new List<List<int>>();
|
||||
|
||||
public static HeightMap LoadHeightMap(string filename, out Tuple<int, int> outStartPoint, out Tuple<int, int> outEndPoint)
|
||||
{
|
||||
HeightMap hm = new HeightMap();
|
||||
Tuple<int, int>? startPoint = null;
|
||||
Tuple<int, int>? endPoint = null;
|
||||
|
||||
using (StreamReader reader = System.IO.File.OpenText(filename))
|
||||
{
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string? line = reader.ReadLine();
|
||||
if (line == null) throw new InvalidDataException();
|
||||
|
||||
List<int> row = new List<int>();
|
||||
|
||||
foreach (char c in line)
|
||||
{
|
||||
if (!char.IsLetter(c)) throw new InvalidDataException();
|
||||
|
||||
if (c == 'S')
|
||||
{
|
||||
row.Add('a' - 'a');
|
||||
startPoint = new Tuple<int, int>(row.Count - 1, hm.heightmap.Count);
|
||||
}
|
||||
else if (c == 'E')
|
||||
{
|
||||
row.Add('z' - 'a');
|
||||
endPoint = new Tuple<int, int>(row.Count - 1, hm.heightmap.Count);
|
||||
}
|
||||
else
|
||||
{
|
||||
row.Add(c - 'a');
|
||||
}
|
||||
}
|
||||
|
||||
hm.heightmap.Add(row);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (startPoint == null || endPoint == null) throw new InvalidDataException();
|
||||
outStartPoint = startPoint;
|
||||
outEndPoint = endPoint;
|
||||
|
||||
return hm;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user