mykdavies

AOC2021 Day23

Dec 23rd, 2021
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 7.78 KB | None | 0 0
  1. /// Turn 3x3x3 blocks of light on and off as instructed.
  2. /// First limited to -50 to 50 in each direction.
  3. /// Then it most definitely isn't :-(
  4.  
  5. // ignore_for_file: unused_import
  6.  
  7. import 'dart:io';
  8. import 'dart:math';
  9. import 'dart:typed_data';
  10. import 'package:more/more.dart';
  11. import 'package:collection/collection.dart';
  12.  
  13. // late Map<String, int> store;
  14. // setScore(String pos, int score) => store[pos] = score;
  15. // getScore(String pos) => store[pos] ?? 0;
  16.  
  17. setup() {
  18.   // store = {};
  19. }
  20.  
  21. late Graph graph;
  22. int buildPaths1(String start) {
  23.   setup();
  24.   graph = Graph();
  25.   var end = '00AA0BB0CC0DD00AABBCCDD';
  26.   aStarSearch(graph, start, end);
  27.   print('route');
  28.   String? here = end;
  29.   var cost = 0;
  30.   while (here != null) {
  31.     print(here);
  32.     var there = here;
  33.     here = cameFrom[here];
  34.     if (here != null) cost += graph.edgeBetween(here, there)!.weight;
  35.   }
  36.   return cost;
  37. }
  38. // buildPaths() {
  39. //   setup();
  40. //   var queue = ['00BA0CD0BC0DA00'];
  41. //   graph = Graph();
  42. //   while (queue.isNotEmpty) {
  43. //     if (queue.length % 100 == 0) print(queue.length);
  44. //     var here = queue.removeLast();
  45. //     // graph.nodes[here] = Node();
  46.  
  47. //     for (var next in validMoves(here)) {
  48. //       var n = next.first;
  49. //       var c = next.second;
  50. //       // if (!graph.nodes.containsKey(n)) {
  51. //       //   graph.nodes[n] = Node();
  52. //       // }
  53. //       // graph.addEdge(here, n, c);
  54. //       queue.add(n);
  55. //     }
  56. //   }
  57. // }
  58.  
  59. var homes = '..AA.BB.CC.DD..AABBCCDD';
  60. // Use Points as they use ==.
  61. var links = {
  62.   Point(0, 1): 1,
  63.   Point(1, 2): 2,
  64.   Point(1, 4): 2,
  65.   Point(2, 3): 1,
  66.   Point(2, 4): 2,
  67.   Point(3, 15): 1,
  68.   Point(15, 16): 1,
  69.   Point(4, 5): 2,
  70.   Point(4, 7): 2,
  71.   Point(5, 6): 1,
  72.   Point(5, 7): 2,
  73.   Point(6, 17): 1,
  74.   Point(17, 18): 1,
  75.   Point(7, 8): 2,
  76.   Point(7, 10): 2,
  77.   Point(8, 9): 1,
  78.   Point(8, 10): 2,
  79.   Point(9, 19): 1,
  80.   Point(19, 20): 1,
  81.   Point(10, 11): 2,
  82.   Point(10, 13): 2,
  83.   Point(11, 12): 1,
  84.   Point(11, 13): 2,
  85.   Point(12, 21): 1,
  86.   Point(21, 22): 1,
  87.   Point(13, 14): 1
  88. };
  89. // Roughly where they are in the diagram except alternating arms are moved
  90. // upward to make the manhattan distance more convincing.
  91. var posLocs = <int, Point<int>>{
  92.   0: Point(0, 2),
  93.   1: Point(1, 2),
  94.   2: Point(2, 1),
  95.   3: Point(2, 0),
  96.   4: Point(3, 2),
  97.   5: Point(4, 3),
  98.   6: Point(4, 4),
  99.   7: Point(5, 2),
  100.   8: Point(6, 1),
  101.   9: Point(6, 0),
  102.   10: Point(7, 2),
  103.   11: Point(8, 3),
  104.   12: Point(8, 4),
  105.   13: Point(9, 2),
  106.   14: Point(10, 2),
  107.   15: Point(2, -1),
  108.   16: Point(2, -2),
  109.   17: Point(4, 5),
  110.   18: Point(4, 6),
  111.   19: Point(6, -1),
  112.   20: Point(6, -2),
  113.   21: Point(8, 5),
  114.   22: Point(8, 6)
  115. };
  116.  
  117. var weights = {'A': 1, 'B': 10, 'C': 100, 'D': 1000};
  118. var neighbours = {
  119.   0: [1],
  120.   1: [0, 2, 4],
  121.   2: [1, 3, 4],
  122.   3: [2, 15],
  123.   4: [1, 2, 5, 7],
  124.   5: [4, 6, 7],
  125.   6: [5, 17],
  126.   7: [4, 5, 8, 10],
  127.   8: [7, 9, 10],
  128.   9: [8, 19],
  129.   10: [7, 8, 11, 13],
  130.   11: [10, 12, 13],
  131.   12: [11, 21],
  132.   13: [10, 11, 14],
  133.   14: [13],
  134.   15: [3, 16],
  135.   16: [15],
  136.   17: [6, 18],
  137.   18: [17],
  138.   19: [9, 20],
  139.   20: [19],
  140.   21: [12, 22],
  141.   22: [21]
  142. };
  143.  
  144. Iterable<Tuple2<String, int>> validMoves(String pos) sync* {
  145.   for (var i in 0.to(pos.length)) {
  146.     if (!'ABCD'.contains(pos[i])) continue;
  147.     for (var ni in neighbours[i]!) {
  148.       if (ni < 0 || ni >= pos.length) continue;
  149.       // Only move into empty cells
  150.       if (pos[ni] != '0') continue;
  151.       // We cannot move from an open space into the wrong home.
  152.       if (homes[i] == '.' && homes[ni] != '.' && homes[ni] != pos[i]) continue;
  153.       var l = links.keys
  154.           .firstWhereOrNull((e) => (e == Point(i, ni) || (e == Point(ni, i))));
  155.       if (l == null) continue;
  156.       // All good.
  157.       var ret =
  158.           pos.replaceRange(i, i + 1, pos[ni]).replaceRange(ni, ni + 1, pos[i]);
  159.       yield Tuple2(ret, links[l]! * weights[pos[i]]!);
  160.     }
  161.   }
  162. }
  163.  
  164. class Node {
  165.   int distance;
  166.   String? previous;
  167.  
  168.   Node([this.distance = 99999999999999999, this.previous]);
  169. }
  170.  
  171. class Edge {
  172.   String from, to;
  173.   int weight;
  174.   Edge(this.from, this.to, this.weight);
  175. }
  176.  
  177. class Graph {
  178.   // Use String keys for nodes and edges to make look-up easier.
  179.   var nodes = <String, Node>{};
  180.   var edges = <String, Edge>{};
  181.   var connected = SetMultimap<String, String>();
  182.   Graph();
  183.  
  184.   // Building algo means that both directions get created separately.
  185.   addEdge(from, to, weight) {
  186.     edges["$from/$to"] = Edge(from, to, weight);
  187.     connected.add(from, to);
  188.     return "$from/$to";
  189.   }
  190.  
  191.   // Proper version of this function.
  192.   // List<String> neighboursOf(String node) => connected[node].toList();
  193.   List<String> neighboursOf(String node) {
  194.     if (nodes[node] != null) return connected[node].toList();
  195.     nodes[node] = Node();
  196.     var ret = <String>[];
  197.     for (var v in validMoves(node)) {
  198.       if (edgeBetween(node, v.first) == null) addEdge(node, v.first, v.second);
  199.       ret.add(v.first);
  200.     }
  201.     return ret;
  202.   }
  203.  
  204.   Edge? edgeBetween(String node1, String node2) => edges["$node1/$node2"];
  205. }
  206.  
  207. // `cameFrom` is global to allow callers to query it.
  208. var cameFrom = <String, String?>{};
  209. dijkstra(Graph graph, start, end) {
  210.   cameFrom = <String, String?>{};
  211.   var costSoFar = <String, int>{};
  212.   cameFrom[start] = null;
  213.   costSoFar[start] = 0;
  214.   var frontier = PriorityQueue<String>(
  215.       (a, b) => (costSoFar[a]!).compareTo((costSoFar[b]!)));
  216.   frontier.add(start);
  217.   while (frontier.isNotEmpty) {
  218.     var current = frontier.removeFirst();
  219.     if (current == end) break;
  220.     for (var next in graph.neighboursOf(current)) {
  221.       var newCost =
  222.           costSoFar[current]! + graph.edgeBetween(current, next)!.weight;
  223.       if (!costSoFar.containsKey(next) || newCost < costSoFar[next]!) {
  224.         costSoFar[next] = newCost;
  225.         frontier.add(next);
  226.         cameFrom[next] = current;
  227.       }
  228.     }
  229.   }
  230.   return costSoFar;
  231. }
  232.  
  233. // Crude distance to the end point. Used by A* algorithm.
  234. // Must be underestimate.
  235. int heuristic(String locA, String locB) {
  236.   //locB is always 'end', which has distance zero :-) so ignore.
  237.  
  238.   var dist = 0;
  239.   for (var i in 0.to(locA.length)) {
  240.     //look at each non-zero.
  241.     if (locA[i] == '0') continue;
  242.     // if it's in a home, then dist = 0
  243.     if (locA[i] == homes[i]) continue;
  244.     // else get posloc of its location and its home
  245.     var hi = homes.indexOf(locA[i]);
  246.     var pp = posLocs[i]!;
  247.     var hp = posLocs[hi]!;
  248.     //and calc manhattan distance.
  249.     var md = (pp.x - hp.x).abs() + (pp.y - hp.y).abs();
  250.     // Apply weight for the type.
  251.     dist += (weights[locA[i]]! * md);
  252.   }
  253.   return dist;
  254. }
  255.  
  256. aStarSearch(Graph graph, String start, String end) {
  257.   var costSoFar = <String, int>{};
  258.   // Build a lookup table of priorities for cells.
  259.   var _heur = <String, int>{};
  260.   var frontier =
  261.       PriorityQueue<String>((a, b) => _heur[a]!.compareTo(_heur[b]!));
  262.   frontier.add(start);
  263.   cameFrom = <String, String?>{};
  264.   cameFrom[start] = null;
  265.   costSoFar[start] = 0;
  266.   while (frontier.isNotEmpty) {
  267.     if (frontier.length % 100 == 0) {
  268.       print(frontier.length);
  269.       print(frontier.first);
  270.     }
  271.     // print(frontier.length);
  272.     var current = frontier.removeFirst();
  273.     if (current == end) {
  274.       break;
  275.     }
  276.     for (var next in graph.neighboursOf(current)) {
  277.       var newCost =
  278.           costSoFar[current]! + graph.edgeBetween(current, next)!.weight;
  279.       if (!costSoFar.containsKey(next) || newCost < costSoFar[next]!) {
  280.         costSoFar[next] = newCost;
  281.         _heur[next] = newCost + heuristic(next, end);
  282.         frontier.add(next);
  283.         cameFrom[next] = current;
  284.       }
  285.     }
  286.   }
  287.   return costSoFar;
  288. }
  289.  
  290. main() {
  291.   // var lines = File('data/data22_01.txt').readAsLinesSync();
  292.   // var t = DateTime.now();
  293.   // print('${DateTime.now().difference(t).inMilliseconds} milliseconds');
  294. }
  295.  
Advertisement
Add Comment
Please, Sign In to add comment