Advertisement
mykdavies

AOC 2022 Day 16

Dec 16th, 2022
1,500
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 5.94 KB | None | 0 0
  1. import 'package:collection/collection.dart';
  2. //import 'package:more/more.dart';
  3.  
  4. class Node implements Comparable {
  5.   String id;
  6.   int rate;
  7.   int totalRate = 0;
  8.   var edges = <Node, int>{};
  9.   var timeTo = <Node, int>{};
  10.   var routeTo = <Node, List<Node>>{};
  11.   Node(this.id) : rate = 0;
  12.   @override
  13.   String toString() {
  14.     var v = edges.entries.map((e) => "${e.key.id} [${e.value}]").join(" ");
  15.     return '$id ($rate) $v';
  16.   }
  17.  
  18.   @override
  19.   int compareTo(other) {
  20.     //if (!other is Node) throw Exception('Not a node: $other');
  21.     return id.compareTo(other.id);
  22.   }
  23.  
  24.   @override
  25.   int get hashCode => id.hashCode;
  26.  
  27.   @override
  28.   bool operator ==(Object other) => other is Node && other.id == id;
  29. }
  30.  
  31. class Graph {
  32.   var nodes = <Node>{};
  33.   Graph();
  34.  
  35.   Node ensure(Node n) {
  36.     if (nodes.contains(n)) return nodes.firstWhere((e) => e == n);
  37.     nodes.add(n);
  38.     return n;
  39.   }
  40.  
  41.   // Building algo means that both directions get created separately.
  42.   void addEdge(Node from, Node to, int weight) {
  43.     from.edges[to] = weight;
  44.   }
  45.  
  46.   List<Node> neighboursOf(Node node) => node.edges.keys.toList();
  47.  
  48.   simplify() {
  49.     for (var n in nodes.toList()) {
  50.       if (n.rate == 0 && n.edges.length == 2) {
  51.         var na = n.edges.keys.first;
  52.         var nb = n.edges.keys.last;
  53.         var d = n.edges.values.sum;
  54.         na.edges.remove(n);
  55.         na.edges[nb] = d;
  56.         nb.edges.remove(n);
  57.         nb.edges[na] = d;
  58.         nodes.remove(n);
  59.       }
  60.     }
  61.   }
  62. }
  63.  
  64. var cameFrom = <Node, Node>{};
  65. // Returns map of Points examined and their distances from the start.
  66. Map<Node, int> dijkstra(Graph graph, Node start, Node end) {
  67.   cameFrom = {start: Node('')};
  68.   var bestCost = {start: 0};
  69.   var frontier =
  70.       PriorityQueue<Node>((a, b) => (bestCost[a]!).compareTo((bestCost[b]!)));
  71.   frontier.add(start);
  72.   while (frontier.isNotEmpty) {
  73.     var current = frontier.removeFirst();
  74.     if (current == end) break;
  75.     // could use this shortcut here if the Part 2 search takes too long
  76.     // if (graph.nodes[current].height = 0) break;
  77.     for (var next in current.edges.keys) {
  78.       var newCost = bestCost[current]! + current.edges[next]!;
  79.       if (!bestCost.containsKey(next) || newCost < bestCost[next]!) {
  80.         bestCost[next] = newCost;
  81.         frontier.add(next);
  82.         cameFrom[next] = current;
  83.       }
  84.     }
  85.   }
  86.   return bestCost;
  87. }
  88.  
  89. Graph buildGraph(List<String> lines) {
  90.   var g = Graph();
  91.   var reD = RegExp(r'\d+');
  92.   var re = RegExp(r'[A-Z]{2}');
  93.   for (var l in lines) {
  94.     var rate = int.parse(reD.stringMatch(l)!);
  95.     var ms = re.allMatches(l);
  96.     var n = g.ensure(Node(ms.first[0]!));
  97.     n.rate = rate;
  98.     for (var m in ms.skip(1)) {
  99.       var nn = g.ensure(Node(m[0]!));
  100.       g.addEdge(n, nn, 1);
  101.     }
  102.   }
  103.   return g;
  104. }
  105.  
  106. // late Point start, end;
  107. part1(List<String> lines) {
  108.   Graph g = buildGraph(lines);
  109.   g.simplify();
  110.   // Build up list of times between each pair of nodes
  111.   for (var n in g.nodes) {
  112.     var bestTimes = dijkstra(g, n, Node('NOPE'));
  113.     for (var nn in bestTimes.keys.where((e) => e != n)) {
  114.       n.timeTo[nn] = bestTimes[nn]!;
  115.     }
  116.   }
  117.   var ns = g.nodes;
  118.   var start = g.nodes.firstWhere((e) => e.id == 'AA');
  119.   ns.remove(start);
  120.   return bestFlow(start, ns, 0, 0, 0);
  121. }
  122.  
  123. int bestFlow(Node start, Set<Node> ln, int t, int rate, int totalFlow) {
  124.   if (ln.isEmpty) {
  125.     if (t <= 30) totalFlow += rate * (30 - t);
  126.     return totalFlow;
  127.   }
  128.   var maxFlow = totalFlow;
  129.   for (var node in ln) {
  130.     var ns = ln.toSet();
  131.     ns.remove(node);
  132.     var delta = start.timeTo[node]! + 1;
  133.     if (t + delta > 30) {
  134.       maxFlow = [maxFlow, totalFlow + rate * (30 - t)].max;
  135.       break;
  136.     }
  137.     maxFlow = [
  138.       maxFlow,
  139.       bestFlow(node, ns, t + delta, rate + node.rate, totalFlow + rate * delta)
  140.     ].max;
  141.   }
  142.   return maxFlow;
  143. }
  144.  
  145. int bestFlow2(int lim, Node s1, Node s2, List<Node> ln, int t1, int t2, int t,
  146.     int rate, int totalFlow) {
  147.   //print('t: $t\t ($t1, $t2) ${s1.id} ${s2.id} \t $rate \t $totalFlow');
  148.   if (t >= lim) {
  149.     return totalFlow;
  150.   }
  151.   if (t < t1 && t < t2) {
  152.     return bestFlow2(lim, s1, s2, ln, t1, t2, t + 1, rate, totalFlow + rate);
  153.   }
  154.   if (t1 > lim && t2 > lim) {
  155.     return totalFlow + rate * (lim - t);
  156.   }
  157.  
  158.   // t = t1 or t2 and we have time, and we have nodes.
  159.   // Each branch below takes zero time, but opens the valve (increases rate
  160.   // but doesn't count it yet), finds a next target, and updates teh target time
  161.   //
  162.   var maxFlow = totalFlow;
  163.   if (t == t1) {
  164.     rate += s1.rate;
  165.     // odd exception to add indicating my logic is bad...
  166.     if (ln.isEmpty) {
  167.       return bestFlow2(
  168.           lim, s1, s2, ln, t + 1000, t2, t + 1, rate, totalFlow + rate);
  169.     }
  170.     for (var node in ln) {
  171.       var ns = ln.toList();
  172.       ns.remove(node);
  173.       maxFlow = [
  174.         maxFlow,
  175.         bestFlow2(
  176.             lim, node, s2, ns, t + s1.timeTo[node]! + 1, t2, t, rate, totalFlow)
  177.       ].max;
  178.     }
  179.     return maxFlow;
  180.   }
  181.   if (t == t2) {
  182.     rate += s2.rate;
  183.     if (ln.isEmpty) {
  184.       return bestFlow2(
  185.           lim, s1, s2, ln, t1, t2 + 10000, t + 1, rate, totalFlow + rate);
  186.     }
  187.  
  188.     for (var node in ln) {
  189.       var ns = ln.toList();
  190.       ns.remove(node);
  191.       maxFlow = [
  192.         maxFlow,
  193.         bestFlow2(
  194.             lim, s1, node, ns, t1, t + s2.timeTo[node]! + 1, t, rate, totalFlow)
  195.       ].max;
  196.     }
  197.     //return maxFlow;
  198.   }
  199.  
  200.   return maxFlow;
  201. }
  202.  
  203. // Dance with the elephant.
  204. part2(List<String> lines) {
  205.   Graph g = buildGraph(lines);
  206.   g.simplify();
  207.   // Build up list of times between each pair of nodes
  208.   for (var n in g.nodes) {
  209.     var bestTimes = dijkstra(g, n, Node('NOPE'));
  210.     for (var nn in bestTimes.keys.where((e) => e != n)) {
  211.       n.timeTo[nn] = bestTimes[nn]!;
  212.     }
  213.   }
  214.   var ns = g.nodes.toList();
  215.   var start = g.nodes.firstWhere((e) => e.id == 'AA');
  216.   ns.remove(start);
  217.   return bestFlow2(26, start, start, ns, 0, 0, 0, 0, 0);
  218. }
  219.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement