Advertisement
mykdavies

AOC 2022 Day 17

Dec 17th, 2022 (edited)
1,131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Dart 4.53 KB | None | 0 0
  1. /// Drop simple tetris pieces that get moved sideways by jets of air.
  2. /// Both cycle forever. Measure the height of the resulting stack.
  3. /// 1) after 2022 pieces.
  4. /// 2) after 1000000000000 pieces.
  5. ///
  6. import 'package:collection/collection.dart';
  7. import 'package:more/more.dart';
  8.  
  9. class Index {
  10.   final int r;
  11.   final int c;
  12.  
  13.   const Index(this.r, this.c);
  14.  
  15.   @override
  16.   String toString() => 'Index($r, $c)';
  17.  
  18.   @override
  19.   bool operator ==(Object other) =>
  20.       other is Index && r == other.r && c == other.c;
  21.  
  22.   @override
  23.   int get hashCode => Object.hash(r.hashCode, c.hashCode);
  24.  
  25.   Index operator +(Index other) => Index(r + other.r, c + other.c);
  26.   Index operator -(Index other) => Index(r - other.r, c - other.c);
  27.   Index operator *(int n) => Index(r * n, c * n);
  28. }
  29.  
  30. class ListGrid<T> {
  31.   List<List<T>> _grid;
  32.   ListGrid(this._grid);
  33.   factory ListGrid.ofSize(int height, int width, T val) {
  34.     return ListGrid(
  35.         List.generate(height, (index) => List.generate(width, (i) => val)));
  36.   }
  37.   operator [](Index i) => _grid[i.r][i.c];
  38.   operator []=(Index i, T val) => _grid[i.r][i.c] = val;
  39.   int get height => _grid.length;
  40.   int get width => _grid.first.length;
  41.   List<T> row(int r) => _grid[r];
  42.  
  43.   bool isInBounds(Index i) =>
  44.       i.r >= 0 && i.r < height && i.c >= 0 && i.c < width;
  45. }
  46.  
  47. // Starring, in order of appearance. (but upside down).
  48. var rocks = {
  49.   'dash': [Index(0, 0), Index(0, 1), Index(0, 2), Index(0, 3)],
  50.   'plus': [Index(0, 1), Index(1, 0), Index(1, 1), Index(1, 2), Index(2, 1)],
  51.   'bend': [Index(0, 0), Index(0, 1), Index(0, 2), Index(1, 2), Index(2, 2)],
  52.   'pole': [Index(0, 0), Index(1, 0), Index(2, 0), Index(3, 0)],
  53.   'cube': [Index(0, 0), Index(0, 1), Index(1, 0), Index(1, 1)],
  54. };
  55. late ListGrid<String> grid;
  56. var width = 7;
  57.  
  58. canMove(List<Index> rock, Index offset) => rock
  59.     .map((e) => e + offset)
  60.     .every((e) => grid.isInBounds(e) && grid[e] == '.');
  61.  
  62. part1(List<String> lines) => solve(2022, lines);
  63.  
  64. part2(List<String> lines) => solve(1000000000000, lines);
  65.  
  66. int solve(int targetRock, List<String> lines) {
  67.   var shortCutRun = false;
  68.   // Let a decent number of rows build up before checking.
  69.   var nLinesToCheck = 50;
  70.  
  71.   var jets = lines.first
  72.       .split('')
  73.       .map((e) => e == '>' ? Index(0, 1) : Index(0, -1))
  74.       .toList();
  75.   // Generously big. Don't link this to `targetRock` because of part 2 :-)
  76.   grid = ListGrid.ofSize(10000, width, '.');
  77.   var topRow = -1;
  78.   var fall = Index(-1, 0);
  79.   var rockSource = rocks.keys.repeat().iterator;
  80.   // loop through the index rather than the values, for later cycle check.
  81.   var jetSource = 0.to(jets.length).repeat().iterator;
  82.   var seen = <int, List<int>>{};
  83.   var rockCount = 0;
  84.   var topAdd = 0;
  85.   // loop manually to allow adjustment to rockCount for part 2
  86.   while (true) {
  87.     //drop a rock (upwards!)
  88.     var rock = (rockSource..moveNext()).current;
  89.     var row = topRow + 4;
  90.     var col = 2;
  91.     var pos = Index(row, col);
  92.     while (true) {
  93.       var jet = jets[(jetSource..moveNext()).current];
  94.       // blow
  95.       if (canMove(rocks[rock]!, pos + jet)) pos += jet;
  96.       // fall
  97.       if (!(canMove(rocks[rock]!, pos + fall))) {
  98.         break;
  99.       }
  100.       pos += fall;
  101.     }
  102.     topRow = [topRow, rocks[rock]!.map((e) => e.r).max + pos.r].max;
  103.     rocks[rock]!.forEach((e) => grid[e + pos] = '#');
  104.     if (!shortCutRun && jetSource.current == 0 && topRow > nLinesToCheck) {
  105.       var key = (topRow - nLinesToCheck)
  106.           .to(topRow)
  107.           .map((e) => grid.row(e).join(''))
  108.           .join('')
  109.           .hashCode;
  110.       if (seen.containsKey(key)) {
  111.         //we have a repeat
  112.         shortCutRun = true;
  113.         // printIt(grid, Index(topRow, 0));
  114.         // print('repeat ${seen[key]} -- $cycle $rockCount $topRow');
  115.         // print('');
  116.  
  117.         var rDiff = rockCount - seen[key]!.first;
  118.         var topDiff = topRow - seen[key]!.last;
  119.  
  120.         var mult = (targetRock - rockCount) ~/ rDiff;
  121.         rockCount += rDiff * mult;
  122.         topAdd += topDiff * mult;
  123.       } else {
  124.         seen[key] = [rockCount, topRow];
  125.       }
  126.     }
  127.     rockCount += 1;
  128.     if (rockCount >= targetRock) break;
  129.   }
  130.   return topRow + 1 + topAdd;
  131. }
  132.  
  133. printIt(ListGrid grid, Index pos, {shape = const <Index>[]}) {
  134.   for (var r in 0.to(pos.r + 5).reversed) {
  135.     var p = '';
  136.     for (var c in 0.to(grid.width)) {
  137.       p += (shape.contains(Index(r, c) - pos)) ? '@' : grid[Index(r, c)];
  138.     }
  139.     print('${r.toString().padLeft(4)}: #$p#');
  140.   }
  141.   print('      ${'#' * (width + 2)}');
  142.   print('');
  143. }
  144.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement