Advertisement
mykdavies

AOC 2022 Day 22

Dec 22nd, 2022 (edited)
924
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 6.24 KB | None | 0 0
  1. /// Navigate your way around a grid.
  2. /// 1) Following instructions.
  3. /// 2) But first, wrap it round a cube.
  4. ///
  5. import 'dart:math';
  6. import 'package:more/more.dart';
  7.  
  8. var headings = [Point(0, -1), Point(1, 0), Point(0, 1), Point(-1, 0)];
  9. var arrows = ['^', '>', 'v', '<'];
  10.  
  11. part1(List<String> ls) {
  12.   var lines = ls.toList();
  13.  
  14.   var navRules = lines.removeLast();
  15.   lines.removeLast();
  16.   var width = lines.map((e) => e.length).max();
  17.   var height = lines.length;
  18.   var grid = lines.map((e) => e.padRight(width).split('')).toList();
  19.  
  20.   var pos = Point(lines[0].indexOf('.'), 0);
  21.   var heading = 1;
  22.  
  23.   navRules = navRules.replaceAll('R', ' R ').replaceAll('L', ' L ');
  24.   var nr = navRules.split(' ');
  25.   for (var r in nr) {
  26.     if (r == 'L' || r == 'R') {
  27.       heading = (heading + ((r == 'R') ? 1 : -1)) % headings.length;
  28.       continue;
  29.     }
  30.     var step = int.parse(r);
  31.  
  32.     while (step > 0) {
  33.       var np = pos + headings[heading];
  34.       np = Point(np.x % width, np.y % height);
  35.       while (grid[np.y][np.x] == ' ') {
  36.         np = np + headings[heading];
  37.         np = Point(np.x % width, np.y % height);
  38.       }
  39.  
  40.       if (grid[np.y][np.x] == '#') {
  41.         break;
  42.       }
  43.       grid[pos.y][pos.x] = arrows[heading];
  44.       pos = np;
  45.       step -= 1;
  46.     }
  47.   }
  48.   return 1000 * (pos.y + 1) + 4 * (pos.x + 1) + (heading - 1);
  49. }
  50.  
  51. /// OKAY
  52.  
  53. /// Our ideal cube is laid out like this:
  54. ///
  55. ///     T
  56. /// W - S - E - N
  57. ///     B
  58. ///
  59. /// transforms shows what you will see in each direction from the cube as
  60. /// laid out above.
  61. /// NOTE !!!! 0 = N, 1 = E...
  62. /// SO 'S':0 shows that North of S is a T in the same orientation.
  63.  
  64. var transforms = {
  65.   'S': {0: Tf('T', 0), 1: Tf('E', 0), 2: Tf('B', 0), 3: Tf('W', 0)},
  66.   'E': {0: Tf('T', 1), 1: Tf('N', 0), 2: Tf('B', 3), 3: Tf('S', 0)},
  67.   'N': {0: Tf('T', 2), 1: Tf('W', 0), 2: Tf('B', 2), 3: Tf('E', 0)},
  68.   'W': {0: Tf('T', 3), 1: Tf('S', 0), 2: Tf('B', 1), 3: Tf('N', 0)},
  69.   'B': {0: Tf('S', 0), 1: Tf('E', 1), 2: Tf('N', 2), 3: Tf('W', 3)},
  70.   'T': {0: Tf('N', 2), 1: Tf('E', 3), 2: Tf('S', 0), 3: Tf('W', 1)},
  71. };
  72.  
  73. class Face {
  74.   String name;
  75.   int orientation;
  76.   Point<int> origin;
  77.   Face(this.name, this.orientation, this.origin);
  78.   @override
  79.   int get hashCode => name.hashCode;
  80.  
  81.   @override
  82.   bool operator ==(Object other) => other is Face && other.name == name;
  83. }
  84.  
  85. class Tf {
  86.   String name;
  87.   int rotation;
  88.   Tf(this.name, this.rotation);
  89. }
  90.  
  91. part2(List<String> ls) {
  92.   var lines = ls.toList();
  93.  
  94.   var navRules = lines.removeLast();
  95.   lines.removeLast();
  96.   var width = lines.map((e) => e.length).max();
  97.   var height = lines.length;
  98.   var grid = lines.map((e) => e.padRight(width).split('')).toList();
  99.  
  100.   // I think this must be true....
  101.   var faceSize = (width > height) ? width ~/ 4 : width ~/ 3;
  102.  
  103.   var x = [grid.first.indexOf('.'), grid.first.indexOf('#')].min();
  104.   Map<String, Face> faces = buildFaces(x, faceSize, width, height, grid);
  105.  
  106.   //Now have all the faces in `faces` with the origin and required tranforms
  107.   //Define the topmost face to be T, and work from there.
  108.   var face = faces['T']!;
  109.   var pos = Point(x, 0);
  110.   var heading = 1;
  111.  
  112.   navRules = navRules.replaceAll('R', ' R ').replaceAll('L', ' L ');
  113.   var nr = navRules.split(' ');
  114.   for (var r in nr) {
  115.     if (r == 'L' || r == 'R') {
  116.       // print('turn $turn');
  117.       heading = (heading + ((r == 'R') ? 1 : -1)) % headings.length;
  118.       continue;
  119.     }
  120.     // print(pos);
  121.     var step = int.parse(r);
  122.     // print('step $step');
  123.  
  124.     while (step > 0) {
  125.       var np = pos + headings[heading];
  126.  
  127.       //HERE COMES THE MAGIC. We're moving to another face -- recalibrate!!!
  128.       if (np.x < face.origin.x ||
  129.           np.x >= face.origin.x + faceSize ||
  130.           np.y < face.origin.y ||
  131.           np.y >= face.origin.y + faceSize) {
  132.         var tf = transforms[face.name]![(heading - face.orientation) % 4]!;
  133.         var nf = faces[tf.name]!;
  134.  
  135.         // Base case -- the point relative to the old face
  136.         var tnp = Point(np.x % faceSize, np.y % faceSize);
  137.         var relOrient = (nf.orientation - face.orientation - tf.rotation) % 4;
  138.         var theading = (heading + relOrient) % 4;
  139.         if (relOrient == 0) {
  140.           // Same orientation, so nothing to do
  141.         } else if (relOrient == 1) {
  142.           tnp = Point(faceSize - 1 - tnp.y, tnp.x);
  143.         } else if (relOrient == 2) {
  144.           tnp = Point(faceSize - 1 - tnp.x, faceSize - 1 - tnp.y);
  145.         } else if (relOrient == 3) {
  146.           tnp = Point(tnp.y, faceSize - 1 - tnp.x);
  147.         }
  148.         // now index it relative to the new face.
  149.         tnp = tnp + nf.origin;
  150.         // is it about to hit a '#'? Stop on this face instead and break.
  151.         if (grid[tnp.y][tnp.x] == '#') break;
  152.  
  153.         //Otherwise make the move.
  154.         face = faces[tf.name]!;
  155.         np = tnp;
  156.         heading = theading;
  157.       }
  158.  
  159.       if (grid[np.y][np.x] == '#') break;
  160.  
  161.       // Just a normal move.
  162.       grid[pos.y][pos.x] = arrows[heading];
  163.       pos = np;
  164.       step -= 1;
  165.     }
  166.     //grid[pos.y][pos.x] = '*';
  167.     //grid.forEach((e) => print(e.join()));
  168.   }
  169.  
  170.   // change the sum
  171.   return 1000 * (pos.y + 1) + 4 * (pos.x + 1) + ((heading - 1) % 4);
  172. }
  173.  
  174. // READ THE ACTUAL GRID
  175. // find a face. call it `T`. push its neighbours into a queue with their
  176. // directions and transforms
  177. Map<String, Face> buildFaces(
  178.     int x, int faceSize, int width, int height, List<List<String>> grid) {
  179.   var queue = [Face('T', 0, Point(x, 0))];
  180.   var faces = <String, Face>{};
  181.   var dirs = [Point(0, -1), Point(1, 0), Point(0, 1), Point(-1, 0)];
  182.   while (queue.isNotEmpty) {
  183.     var face = queue.removeAt(0);
  184.     if (faces.containsKey(face.name)) continue;
  185.     faces[face.name] = face;
  186.     for (var n in [1, 2, 3]) {
  187.       // right, down, left
  188.       var d = face.origin + dirs[n] * faceSize;
  189.       if (d.x < 0 || d.x + faceSize > width || d.y + faceSize > height) {
  190.         continue;
  191.       }
  192.       if (grid[d.y][d.x] != ' ') {
  193.         var nextFace = transforms[face.name]![(n - face.orientation) % 4]!;
  194.         var nextName = nextFace.name;
  195.         var nextOrient = (nextFace.rotation + face.orientation) % 4;
  196.         var nextOrigin = d;
  197.         queue.add(Face(nextName, nextOrient, nextOrigin));
  198.       }
  199.     }
  200.   }
  201.   return faces;
  202. }
  203.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement