diff --git a/advent-of-code-2022.csproj b/advent-of-code-2022.csproj
index cede580..0b5b7b4 100644
--- a/advent-of-code-2022.csproj
+++ b/advent-of-code-2022.csproj
@@ -69,6 +69,7 @@
+
diff --git a/inputs/16.txt b/inputs/16.txt
index bef9be8..8ecdc50 100644
--- a/inputs/16.txt
+++ b/inputs/16.txt
@@ -1 +1,60 @@
-A20D5080210CE4BB9BAFB001BD14A4574C014C004AE46A9B2E27297EECF0C013F00564776D7E3A825CAB8CD47B6C537DB99CD746674C1000D29BBC5AC80442966FB004C401F8771B61D8803D0B22E4682010EE7E59ACE5BC086003E3270AE4024E15C8010073B2FAD98E004333F9957BCB602E7024C01197AD452C01295CE2DC9934928B005DD258A6637F534CB3D89A944230043801A596B234B7E58509E88798029600BCF5B3BA114F5B3BA10C9E77BAF20FA4016FCDD13340118B929DD4FD54E60327C00BEB7002080AA850031400D002369400B10034400F30021400F20157D804AD400FE00034E000A6D001EB2004E5C00B9AE3AC3C300470029091ACADBFA048D656DFD126792187008635CD736B3231A51BA5EBDF42D4D299804F26B33C872E213C840022EC9C21FFB34EDE7C559C8964B43F8AD77570200FC66697AFEB6C757AC0179AB641E6AD9022006065CEA714A4D24C0179F8E795D3078026200FC118EB1B40010A8D11EA27100990200C45A83F12C401A8611D60A0803B1723542889537EFB24D6E0844004248B1980292D608D00423F49F9908049798B4452C0131006230C14868200FC668B50650043196A7F95569CF6B663341535DCFE919C464400A96DCE1C6B96D5EEFE60096006A400087C1E8610A4401887D1863AC99F9802DC00D34B5BCD72D6F36CB6E7D95EBC600013A88010A8271B6281803B12E124633006A2AC3A8AC600BCD07C9851008712DEAE83A802929DC51EE5EF5AE61BCD0648028596129C3B98129E5A9A329ADD62CCE0164DDF2F9343135CCE2137094A620E53FACF37299F0007392A0B2A7F0BA5F61B3349F3DFAEDE8C01797BD3F8BC48740140004322246A8A2200CC678651AA46F09AEB80191940029A9A9546E79764F7C9D608EA0174B63F815922999A84CE7F95C954D7FD9E0890047D2DC13B0042488259F4C0159922B0046565833828A00ACCD63D189D4983E800AFC955F211C700
\ No newline at end of file
+Valve NQ has flow rate=0; tunnels lead to valves SU, XD
+Valve AB has flow rate=0; tunnels lead to valves XD, TE
+Valve IA has flow rate=0; tunnels lead to valves CS, WF
+Valve WD has flow rate=0; tunnels lead to valves DW, II
+Valve XD has flow rate=10; tunnels lead to valves AB, NQ, VT, SC, MU
+Valve SL has flow rate=0; tunnels lead to valves RP, DS
+Valve FQ has flow rate=15; tunnels lead to valves EI, YC
+Valve KF has flow rate=0; tunnels lead to valves FL, QP
+Valve QP has flow rate=0; tunnels lead to valves KF, RP
+Valve DS has flow rate=0; tunnels lead to valves SL, AA
+Valve IK has flow rate=0; tunnels lead to valves XC, AA
+Valve HQ has flow rate=0; tunnels lead to valves VM, WV
+Valve WR has flow rate=0; tunnels lead to valves WV, HF
+Valve HH has flow rate=20; tunnels lead to valves PI, CF, CN, NF, AR
+Valve DW has flow rate=19; tunnels lead to valves KD, WD, HS
+Valve RP has flow rate=14; tunnels lead to valves SL, QP, BH, LI, WP
+Valve EC has flow rate=0; tunnels lead to valves NF, XC
+Valve AA has flow rate=0; tunnels lead to valves NH, ES, UC, IK, DS
+Valve VM has flow rate=18; tunnel leads to valve HQ
+Valve NF has flow rate=0; tunnels lead to valves HH, EC
+Valve PS has flow rate=0; tunnels lead to valves AR, SU
+Valve IL has flow rate=0; tunnels lead to valves XC, KZ
+Valve WP has flow rate=0; tunnels lead to valves CS, RP
+Valve WF has flow rate=0; tunnels lead to valves FL, IA
+Valve XW has flow rate=0; tunnels lead to valves OL, NL
+Valve EH has flow rate=0; tunnels lead to valves UK, YR
+Valve UC has flow rate=0; tunnels lead to valves AA, FL
+Valve CS has flow rate=3; tunnels lead to valves IA, CN, LD, RJ, WP
+Valve AR has flow rate=0; tunnels lead to valves PS, HH
+Valve CF has flow rate=0; tunnels lead to valves HH, FL
+Valve NH has flow rate=0; tunnels lead to valves AA, LD
+Valve RJ has flow rate=0; tunnels lead to valves DJ, CS
+Valve XC has flow rate=17; tunnels lead to valves IL, EC, YR, IK, DJ
+Valve TE has flow rate=24; tunnels lead to valves AB, YA
+Valve CN has flow rate=0; tunnels lead to valves HH, CS
+Valve KD has flow rate=0; tunnels lead to valves DW, UK
+Valve SC has flow rate=0; tunnels lead to valves EI, XD
+Valve MU has flow rate=0; tunnels lead to valves XD, YP
+Valve SU has flow rate=22; tunnels lead to valves PS, LI, II, NQ
+Valve FL has flow rate=8; tunnels lead to valves KF, WF, CF, UC, HS
+Valve OL has flow rate=4; tunnels lead to valves KZ, HF, XW
+Valve EI has flow rate=0; tunnels lead to valves FQ, SC
+Valve NL has flow rate=0; tunnels lead to valves XW, UK
+Valve YP has flow rate=21; tunnels lead to valves YA, MU, YC
+Valve BH has flow rate=0; tunnels lead to valves VT, RP
+Valve II has flow rate=0; tunnels lead to valves SU, WD
+Valve YA has flow rate=0; tunnels lead to valves TE, YP
+Valve HS has flow rate=0; tunnels lead to valves FL, DW
+Valve DJ has flow rate=0; tunnels lead to valves RJ, XC
+Valve KZ has flow rate=0; tunnels lead to valves OL, IL
+Valve YR has flow rate=0; tunnels lead to valves EH, XC
+Valve UK has flow rate=7; tunnels lead to valves KD, NL, EH
+Valve YC has flow rate=0; tunnels lead to valves FQ, YP
+Valve ES has flow rate=0; tunnels lead to valves PI, AA
+Valve LI has flow rate=0; tunnels lead to valves SU, RP
+Valve LD has flow rate=0; tunnels lead to valves NH, CS
+Valve VT has flow rate=0; tunnels lead to valves BH, XD
+Valve PI has flow rate=0; tunnels lead to valves ES, HH
+Valve WV has flow rate=11; tunnels lead to valves WR, HQ
+Valve HF has flow rate=0; tunnels lead to valves OL, WR
\ No newline at end of file
diff --git a/inputs/16a.txt b/inputs/16a.txt
new file mode 100644
index 0000000..54ee23d
--- /dev/null
+++ b/inputs/16a.txt
@@ -0,0 +1,10 @@
+Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
+Valve BB has flow rate=13; tunnels lead to valves CC, AA
+Valve CC has flow rate=2; tunnels lead to valves DD, BB
+Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
+Valve EE has flow rate=3; tunnels lead to valves FF, DD
+Valve FF has flow rate=0; tunnels lead to valves EE, GG
+Valve GG has flow rate=0; tunnels lead to valves FF, HH
+Valve HH has flow rate=22; tunnel leads to valve GG
+Valve II has flow rate=0; tunnels lead to valves AA, JJ
+Valve JJ has flow rate=21; tunnel leads to valve II
\ No newline at end of file
diff --git a/src/16.cs b/src/16.cs
new file mode 100644
index 0000000..13a7c9a
--- /dev/null
+++ b/src/16.cs
@@ -0,0 +1,197 @@
+using System.Text.RegularExpressions;
+
+namespace aoc2022;
+
+internal partial class Day16 : Day
+{
+ private class valve : IEquatable
+ {
+ public string name = string.Empty;
+ public int flowRate = 0;
+ public List connectedValveNames = new();
+ public readonly List connectedValves = new();
+ public bool isOpen = false;
+
+ public valve()
+ {
+
+ }
+
+ public bool Equals(valve? other) => name == other?.name;
+ public override bool Equals(object? obj) => obj is valve other && Equals(other);
+ public override int GetHashCode() => name.GetHashCode();
+ public static bool operator ==(valve left, valve right) => left.Equals(right);
+ public static bool operator !=(valve left, valve right) => !left.Equals(right);
+
+ public override string ToString() => $"[{name}] {flowRate} -> {string.Join(", ", connectedValveNames)}";
+ }
+ private readonly List valves = new();
+
+ [GeneratedRegex("Valve ([^ ]+) has flow rate=(\\d+); tunnels? leads? to valves? (.+)", RegexOptions.Compiled)]
+ private static partial Regex LineRegex();
+
+ internal override void Parse()
+ {
+ var r = LineRegex();
+ foreach (var line in Util.Parsing.ReadAllLines("16"))
+ {
+ var match = r.Match(line);
+ valves.Add(new valve
+ {
+ name = match.Groups[1].Value,
+ flowRate = int.Parse(match.Groups[2].Value),
+ connectedValveNames = new List(match.Groups[3].Value.Split(", ")),
+ });
+ }
+
+ foreach (var valve in valves)
+ {
+ foreach (var connected in valve.connectedValveNames)
+ {
+ valve.connectedValves.Add(valves.First(v => v.name == connected));
+ }
+ }
+ }
+
+ private readonly Dictionary<(valve, valve), int> distanceMap = new();
+ private int shortestPath(valve from, valve to)
+ {
+ if (from == to)
+ {
+ return 0;
+ }
+
+ if (distanceMap.ContainsKey((from, to)))
+ {
+ return distanceMap[(from, to)];
+ }
+
+ Dictionary d = new() {{from, 0}};
+ HashSet unvisited = new(valves.Where(v => v != from));
+ valve current = from;
+ while (true)
+ {
+ int dist = d[current!];
+ foreach (var v in unvisited.Where(v => current!.connectedValves.Contains(v)))
+ {
+ if (!d.ContainsKey(v) || d[v] > dist + 1)
+ {
+ d[v] = dist + 1;
+ }
+ }
+
+ unvisited.Remove(current!);
+ if (current! == to)
+ {
+ break;
+ }
+
+ current = unvisited.MinBy(v => !d.ContainsKey(v) ? int.MaxValue : d[v])!;
+ }
+
+ distanceMap[(from, to)] = d[to];
+ return d[to];
+ }
+
+ private readonly Dictionary<(int, valve, ICollection), int> routes = new();
+
+ private int findBestRoute(int timeLeft, valve startValve, ICollection openValves)
+ {
+ // if (routes.TryGetValue((timeLeft, startValve, openValves), out int retval))
+ // {
+ // return retval;
+ // }
+
+ if (timeLeft <= 2 || valves.All(v => v.isOpen || v.flowRate == 0))
+ {
+ // routes.Add((timeLeft, startValve, openValves), 0);
+ return 0;
+ }
+
+ startValve.isOpen = true;
+
+ var pq = new PriorityQueue();
+ foreach (var v in valves.Where(v => v is {isOpen: false, flowRate: > 0}))
+ {
+ var dist = shortestPath(startValve, v);
+ if (dist + 1 >= timeLeft)
+ {
+ continue;
+ }
+
+ var minutesValveContributes = (timeLeft - dist - 1);
+ var priority = v.flowRate * minutesValveContributes;
+ // var nextOpen = new HashSet(openValves) {v};
+ var totalFlow = findBestRoute(minutesValveContributes, v, openValves);
+
+ pq.Enqueue(v, -(priority + totalFlow));
+ }
+
+ startValve.isOpen = false;
+ pq.TryDequeue(out valve _, out int p);
+ // routes.Add((timeLeft, startValve, openValves), -p);
+ return -p;
+ }
+
+ private int findBestRouteDuo(int timeLeftA, valve startValveA, int timeLeftB, valve startValveB, ICollection openValves)
+ {
+ // if (timeLeftB > timeLeftA)
+ if (timeLeftA <= 2)
+ {
+ (timeLeftA, startValveA, timeLeftB, startValveB) = (timeLeftB, startValveB, timeLeftA, startValveA);
+ }
+
+ // if (routes.TryGetValue((timeLeftA, startValveA, openValves), out int retval))
+ // {
+ // return retval;
+ // }
+
+ if (timeLeftA <= 2 || valves.All(v => v.isOpen || v.flowRate == 0))
+ {
+ // routes.Add((timeLeftA, startValveA, openValves), 0);
+ return 0;
+ }
+
+ var pq = new PriorityQueue();
+ foreach (var v in valves.Where(v => v is {isOpen: false, flowRate: > 0}))
+ {
+ var dist = shortestPath(startValveA, v);
+ if (dist + 1 >= timeLeftA)
+ {
+ continue;
+ }
+
+ var minutesValveContributes = (timeLeftA - dist - 1);
+ var priority = v.flowRate * minutesValveContributes;
+ // var nextOpen = new HashSet(openValves) {v};
+ v.isOpen = true;
+ int totalFlow = findBestRouteDuo(minutesValveContributes, v, timeLeftB, startValveB, openValves);
+ v.isOpen = false;
+ pq.Enqueue(v, -(priority + totalFlow));
+ }
+
+ pq.TryDequeue(out valve _, out int p);
+ // routes.Add((timeLeftA, startValveA, openValves), -p);
+ return -p;
+ }
+
+ internal override string Part1()
+ {
+ var openValves = new HashSet(valves.Where(v => v.flowRate == 0));
+
+ int timeLeft = 30;
+ valve currentValve = valves.First(v => v.name == "AA");
+ var totalFlow = findBestRoute(timeLeft, currentValve, openValves);
+ return $"<+white>{totalFlow}";
+ }
+
+ internal override string Part2()
+ {
+ var openValves = new HashSet(valves.Where(v => v.flowRate == 0));
+
+ int timeLeft = 26;
+ valve currentValve = valves.First(v => v.name == "AA");
+ var totalFlow = findBestRouteDuo(timeLeft, currentValve, timeLeft, currentValve, openValves);
+ return $"<+white>{totalFlow}";
+ }
+}