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}"; + } +}