diff --git a/advent-of-code-2022.csproj b/advent-of-code-2022.csproj
index 4e07177..1fc185f 100644
--- a/advent-of-code-2022.csproj
+++ b/advent-of-code-2022.csproj
@@ -57,6 +57,7 @@
+
diff --git a/inputs/12.txt b/inputs/12.txt
index 0d41869..2626662 100644
--- a/inputs/12.txt
+++ b/inputs/12.txt
@@ -1,24 +1,41 @@
-rf-RL
-rf-wz
-wz-RL
-AV-mh
-end-wz
-end-dm
-wz-gy
-wz-dm
-cg-AV
-rf-AV
-rf-gy
-end-mh
-cg-gy
-cg-RL
-gy-RL
-VI-gy
-AV-gy
-dm-rf
-start-cg
-start-RL
-rf-mh
-AV-start
-qk-mh
-wz-mh
\ No newline at end of file
+abcccaaaaaaccccccccaaaaaccccccaaaaaaccccccaaaaaaaacccaaaaaaaccaaaacccccccccccccccccccccccccaaaaaacccccccccccccccccccccccccccccaaaaaa
+abcccaaaaaacccccccaaaaaaccccaaaaaaaacccccccaaaaaaaaaaaaaaaaccaaaaacccccccccccccccccccccccccaaaaaacccccccccccccccccccccccccccccaaaaaa
+abccccaaaaacaaaccaaaaaaaacccaaaaaaaaacccccccaaaaaaaaaaaaaaaacaaaaaacccccccccaaacccccccccccaaaaaaaaccccccccccaaccccccccccccccccaaaaaa
+abccccaaaaccaaaaaaaaaaaaacccaaaaaaaaaacccccaaaaaaaaaaaaaaaaaaacaaaacccccccccaaaacccccccccaaaaaaaaaacccccccccaaaccccccccccccccccccaaa
+abcccccccccaaaaaacccaacccccccccaaacaaaccccccaacccccccaaaaaaaaacaacccccccccccaaaacccccccccaaaaaaaaaacccccccccaaaccacaaccccccccccccaaa
+abcccccccccaaaaaacccaacccccccccaaacccccccccccccccccccaaaacaaaacccccccaacaaccaaaccccccccccaccaaaaacacccccccccaaaacaaaaccccccccccccaac
+abccccccccccaaaaacccccccccccccccacccaaaacccccccccccccaaaacccccccccccccaaaacccccccccccaacccccaaaaccccccccjjjjaaaaaaaaaccccccccccccccc
+abccccccccccaaaacccccccccccccccccccaaaaacccccccccccccaaaccccccccccccccaaaaacccccccccaaaaaacccaaccccccccjjjjjjkkaaaacccccccccaacccccc
+abcccccaaccccccccccccccccccccccccccaaaaaacccccccccccccaacccccccccccccaaaaaaccccccccccaaaaaccccccccccccjjjjjjjkkkkaacccccaacaaacccccc
+abccaaaacccccccccccccccccccccccccccaaaaaaccccccccccccccccccccccccccccaaaacaccccccccaaaaaaaccccaacccccjjjjoooookkkkkkkklllaaaaaaacccc
+abccaaaaaacccccccccccccccccccccccccaaaaacccccccccccccccccccccccccccccccaaccccccccccaaaaaaaaccaaaaccccjjjoooooookkkkkkkllllaaaaaacccc
+abcccaaaaacccccccccccccccccccccccccccaaaccccccccaaaacccccccccccccccccccccccccccccccaaaaaaaaccaaaaccccjjooooooooppkkppplllllaccaacccc
+abccaaaaaccccccccccccaccccccccccccccccccccccccccaaaacccccccccccccccccccccccccccccccccaaacacccaaaacccijjooouuuuoppppppppplllccccccccc
+abcccccaacccccccccccaaaaaaaaccccccccccccccccccccaaaaccccaaccccccccaaacccccccccccccaacaaccccccccccccciijoouuuuuuppppppppplllcccaccccc
+abcccccccccccccccccccaaaaaaccccccccccccccccccccccaaccccaaaacccccccaaaaccccccccccaaaaaaccccccccccccciiiiootuuuuuupuuuvvpppllccccccccc
+abcccccccccccccccccccaaaaaaccaaaaacccccccccccccccccccccaaaacccccccaaaaccccccccccaaaaaaccccccccccccciiinnotuuxxxuuuuvvvpppllccccccccc
+abccccccccccccccacccaaaaaaaacaaaaaaacccccccccccccccccccaaaacccccccaaacccccaaaaccaaaaaccccaaccccccciiiinnnttxxxxuuyyyvvqqqllccccccccc
+abcccccccccccaaaaccaaaaaaaaaaaaaaaaaaccaacccccccccccccccccccccccccccccccccaaaacccaaaaaccaaacccccciiinnnnnttxxxxxyyyyvvqqqllccccccccc
+abaaaacccccccaaaaaaaaaaaaaaaaaaaaaaaaaaaacccccccccccccccccccccccccccccccccaaaacccaaaaaacaaaccccciiinnnnttttxxxxxyyyyvvqqmmmccccccccc
+abaaaaccccccccaaaaacccaaaaacaaaaaacaaaaaaccccccccccccccccaaccccccccccccccccaacccccccaaaaaaaaaaciiinnnnttttxxxxxyyyyvvqqqmmmccccccccc
+SbaaaacccccccaaaaaccccaaaaaccaaaaaaaaaaaccccccccccccccccaaacaacccccccccccccccccccccccaaaaaaaaachhhnnntttxxxEzzzzyyvvvqqqmmmccccccccc
+abaaaacccccccaacaacccccaaaaaaaacaaaaaaaaaccccccccccccccccaaaaaccccccccccccccccccccccccaaaaaaacchhhnnntttxxxxxyyyyyyvvvqqmmmdddcccccc
+abaaaacccccccccccccccccccaaaaaacaaaaaaaaaacccccccccccccaaaaaaccccccccaaaccccccccccccccaaaaaaccchhhnnntttxxxxywyyyyyyvvvqqmmmdddccccc
+abaacccccccccccccccccccaaaaaaacccccaaaaaaacccccccccccccaaaaaaaacccccaaaacccccccccccccaaaaaaacaahhhmmmttttxxwwyyyyyyyvvvqqmmmdddccccc
+abcccccccccccccccccccccaaaaaaacaaccaaacccccccccccccccccaacaaaaacccccaaaacccccccccccccaaacaaaaaahhhmmmmtsssswwyywwwwvvvvqqqmmdddccccc
+abcccccccccccccccaaaccccaaaaaaaaaacaaccaaccccccccccccccccaaacaccccccaaaacccccccccccccccccaaaaacahhhmmmmmsssswwywwwwwvvrrqqmmdddccccc
+abcccccccccccccaaaaaaccccaaaaaaaaaccaaaacccccccccccccccccaacccccccccccccccccccccccaaaccccaaaaaaahhhhhmmmmssswwwwwrrrrrrrrmmmmddccccc
+abcccccccccccccaaaaaaccccaaaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccccaaaaaacccccaaaaachhhhhmmmmsswwwwrrrrrrrrrkkmdddccccc
+abccccccccccccccaaaaaccccccaaaaaaaaaaaaaaaccccccccccccccccccccccccccccccccccccccaaaaaaccccaaaaacccchhggmmmssswwrrrrrkkkkkkkkdddacccc
+abccaaaacccccccaaaaacccccccccaaaaaacaaaaacccccccccccccccccccccccccccccccccccccccaaaaaaccccaacaaaccccggggmmsssssrrlkkkkkkkkkdddaccccc
+abccaaaacccccccaaaaacccccccccaaaaaaccccaacccccccccccccccccccccccccccccccccccccccaaaaaccccccccaaccccccgggmllssssrllkkkkkkkeeeddaccccc
+abccaaaacccccccaaacccccccccccaaaaaacccccccccccccccccccaacccccccccccccccccccccccaaaaaacccccccccccccccccggllllssslllkkeeeeeeeeeaaacccc
+abcccaaccccccccaaacaaaccccccaaaaaaaaaaacccccccccccccaaaaaacccccccccccccccccccccaaacaaacccccaacccccccccggglllllllllfeeeeeeeeaaaaacccc
+abccccccccccaaaaaaaaaaccccccccccccaccaaaccacccccccccaaaaaaccccaaccaacccaaccccccaaaaaaacccccaaccccccccccggglllllllfffeeecccaaaaaacccc
+abccccccccccaaaaaaaaacccccccccccccccaaaaaaaccccccccccaaaaaccccaaaaaacccaaaaaaccaaaaaacccaaaaaaaacccccccggggllllfffffccccccaacccccccc
+abcccccccccccaaaaaaacccccccccccccccccaaaaaaccaacccccaaaaaccccccaaaaacccaaaaaacaaaaaaacccaaaaaaaaccccccccgggffffffffccccccccccccccccc
+abccccccccccccaaaaaaacccccccccccccaaaaaaaaacaaaaccccaaaaacaaaaaaaaaacaaaaaaacaaaaaaaaaccccaaaacccccccccccggffffffacccccccccccccccaaa
+abccccccccccccaaaaaaacaaccccccccccaaaaaaaaacaaaacccccaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaacccaaaaacccccccccccaffffaaaaccccccccccccccaaa
+abccccccccccccaaacaaaaaacccccccccccaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaacccaaacaaaccaaaaaacccccccccccccccccaaaccccccccccccccaaa
+abccccccccccccaaccaaaaaccccccccccccccaaaaaaaccccaaaaaaaaaaaaccccaacccccaaaaaacccaaaccccccaaccaacccccccccccccccccaaacccccccccccaaaaaa
+abcccccccccccccccaaaaaaaaccccccccccccaacccacccccccaaaaaaaaaaccccaacccccaaccccccccaccccccccccccccccccccccccccccccccccccccccccccaaaaaa
\ No newline at end of file
diff --git a/inputs/12a.txt b/inputs/12a.txt
new file mode 100644
index 0000000..22afdf5
--- /dev/null
+++ b/inputs/12a.txt
@@ -0,0 +1,5 @@
+Sabqponm
+abcryxxl
+accszExk
+acctuvwj
+abdefghi
\ No newline at end of file
diff --git a/src/12.cs b/src/12.cs
new file mode 100644
index 0000000..741c06c
--- /dev/null
+++ b/src/12.cs
@@ -0,0 +1,211 @@
+namespace aoc2022;
+
+internal class Day12 : Day
+{
+ class point : IEquatable
+ {
+ public bool Equals(point? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return x == other.x && y == other.y;
+ }
+
+ public override int GetHashCode() => HashCode.Combine(x, y);
+
+ public readonly int x;
+ public readonly int y;
+
+ public static point operator +(point p, point other) => new(p.x + other.x, p.y + other.y);
+ public static bool operator ==(point p, point other) => p.x == other.x && p.y == other.y;
+ public static bool operator !=(point p, point other) => !(p == other);
+ public override bool Equals(object? obj) => obj is point p && p == this;
+ public override string ToString() => $"{x},{y}";
+
+ public point(int _x, int _y)
+ {
+ x = _x;
+ y = _y;
+ }
+ }
+
+ class DNode : IEquatable
+ {
+ public bool Equals(DNode? other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return neighbors.Equals(other.neighbors)
+ && distance == other.distance
+ && location.Equals(other.location);
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != this.GetType()) return false;
+ return Equals((DNode) obj);
+ }
+
+ public static bool operator ==(DNode? left, DNode? right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(DNode? left, DNode? right)
+ {
+ return !Equals(left, right);
+ }
+
+ public readonly List neighbors = new();
+ public readonly point location;
+ public long? distance;
+
+ public DNode(point loc)
+ {
+ location = loc;
+ }
+
+ public override int GetHashCode() => HashCode.Combine(neighbors, location);
+ public override string ToString() => $"{location}, {neighbors.Count} neighbor{(neighbors.Count == 1 ? "" : "s")}, dist: {distance ?? -1}";
+ }
+
+ private readonly HashSet allNodes = new();
+ private DNode? startNode;
+ private DNode? goalNode;
+
+ private int[][]? heights;
+ private point? start;
+ private point? goal;
+
+ private readonly point[] offsets = new point[]
+ {
+ new(-1, 0),
+ new(1, 0),
+ new(0, -1),
+ new(0, 1),
+ };
+
+ internal override void Parse()
+ {
+ var lines = new List(Util.Parsing.ReadAllLines("12"));
+ heights = new int[lines.Count][];
+ for (int x = 0; x < lines.Count; x++)
+ {
+ var line = lines[x];
+ heights[x] = new int[line.Length];
+ for (int y = 0; y < line.Length; y++)
+ {
+ var ch = line[y];
+ switch (ch)
+ {
+ case 'S':
+ start = new(x, y);
+ ch = 'a';
+ break;
+ case 'E':
+ goal = new(x, y);
+ ch = 'z';
+ break;
+ }
+
+ heights[x][y] = ch - 'a';
+ }
+ }
+
+ startNode = new DNode(start!);
+ allNodes.Add(startNode);
+
+ var unvisited = new HashSet(allNodes);
+ DNode? current = startNode;
+ while (current != null)
+ {
+ var currHeight = heights[current.location.x][current.location.y];
+ foreach (var offset in offsets)
+ {
+ var test = current.location + offset;
+ if (test.x < 0 || test.y < 0 || test.x >= heights.Length || test.y >= heights[0].Length)
+ {
+ continue;
+ }
+
+ var testHeight = heights[test.x][test.y];
+ if (testHeight <= currHeight + 1)
+ {
+ var found = allNodes.FirstOrDefault(n => n.location == test);
+ if (found == null)
+ {
+ found = new DNode(test);
+ allNodes.Add(found);
+ unvisited.Add(found);
+ }
+ current.neighbors.Add(found);
+ }
+ }
+
+ unvisited.Remove(current);
+ current = unvisited.FirstOrDefault();
+ }
+
+ goalNode = allNodes.First(n => n.location == goal!);
+ }
+
+ private long? FindDistance(DNode from, DNode to)
+ {
+ long? distance = null;
+
+ allNodes.ForEach(n =>
+ {
+ n.distance = null;
+ });
+ from.distance = 0;
+
+ var unvisited = new HashSet(allNodes);
+ DNode? current = from;
+ while (current != null)
+ {
+ foreach (var n in current.neighbors.Where(n => unvisited.Contains(n)))
+ {
+ if (n.distance == null || n.distance > current.distance + 1)
+ {
+ n.distance = current.distance + 1;
+ }
+ }
+
+ unvisited.Remove(current);
+ if (current == to)
+ {
+ distance = current.distance;
+ break;
+ }
+
+ current = unvisited.MinBy(n => n.distance);
+ }
+
+ return distance;
+ }
+
+ internal override string Part1()
+ {
+ var dist = FindDistance(startNode!, goalNode!);
+
+ return $"Shortest distance from start to end: <+white>{dist ?? -1}";
+ }
+
+ internal override string Part2()
+ {
+ long? shortestDist = null;
+ var aNodes = allNodes.Where(n => heights![n.location.x][n.location.y] == 0);
+ foreach (var n in aNodes)
+ {
+ var dist = FindDistance(n, goalNode!);
+ if (shortestDist == null || dist < shortestDist)
+ {
+ shortestDist = dist;
+ }
+ }
+
+ return $"Shortest distance from any minimum-height point to end: <+white>{shortestDist}";
+ }
+}