Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// Drop simple tetris pieces that get moved sideways by jets of air.
- /// Both cycle forever. Measure the height of the resulting stack.
- /// 1) after 2022 pieces.
- /// 2) after 1000000000000 pieces.
- ///
- import 'package:collection/collection.dart';
- import 'package:more/more.dart';
- class Index {
- final int r;
- final int c;
- const Index(this.r, this.c);
- @override
- String toString() => 'Index($r, $c)';
- @override
- bool operator ==(Object other) =>
- other is Index && r == other.r && c == other.c;
- @override
- int get hashCode => Object.hash(r.hashCode, c.hashCode);
- Index operator +(Index other) => Index(r + other.r, c + other.c);
- Index operator -(Index other) => Index(r - other.r, c - other.c);
- Index operator *(int n) => Index(r * n, c * n);
- }
- class ListGrid<T> {
- List<List<T>> _grid;
- ListGrid(this._grid);
- factory ListGrid.ofSize(int height, int width, T val) {
- return ListGrid(
- List.generate(height, (index) => List.generate(width, (i) => val)));
- }
- operator [](Index i) => _grid[i.r][i.c];
- operator []=(Index i, T val) => _grid[i.r][i.c] = val;
- int get height => _grid.length;
- int get width => _grid.first.length;
- List<T> row(int r) => _grid[r];
- bool isInBounds(Index i) =>
- i.r >= 0 && i.r < height && i.c >= 0 && i.c < width;
- }
- // Starring, in order of appearance. (but upside down).
- var rocks = {
- 'dash': [Index(0, 0), Index(0, 1), Index(0, 2), Index(0, 3)],
- 'plus': [Index(0, 1), Index(1, 0), Index(1, 1), Index(1, 2), Index(2, 1)],
- 'bend': [Index(0, 0), Index(0, 1), Index(0, 2), Index(1, 2), Index(2, 2)],
- 'pole': [Index(0, 0), Index(1, 0), Index(2, 0), Index(3, 0)],
- 'cube': [Index(0, 0), Index(0, 1), Index(1, 0), Index(1, 1)],
- };
- late ListGrid<String> grid;
- var width = 7;
- canMove(List<Index> rock, Index offset) => rock
- .map((e) => e + offset)
- .every((e) => grid.isInBounds(e) && grid[e] == '.');
- part1(List<String> lines) => solve(2022, lines);
- part2(List<String> lines) => solve(1000000000000, lines);
- int solve(int targetRock, List<String> lines) {
- var shortCutRun = false;
- // Let a decent number of rows build up before checking.
- var nLinesToCheck = 50;
- var jets = lines.first
- .split('')
- .map((e) => e == '>' ? Index(0, 1) : Index(0, -1))
- .toList();
- // Generously big. Don't link this to `targetRock` because of part 2 :-)
- grid = ListGrid.ofSize(10000, width, '.');
- var topRow = -1;
- var fall = Index(-1, 0);
- var rockSource = rocks.keys.repeat().iterator;
- // loop through the index rather than the values, for later cycle check.
- var jetSource = 0.to(jets.length).repeat().iterator;
- var seen = <int, List<int>>{};
- var rockCount = 0;
- var topAdd = 0;
- // loop manually to allow adjustment to rockCount for part 2
- while (true) {
- //drop a rock (upwards!)
- var rock = (rockSource..moveNext()).current;
- var row = topRow + 4;
- var col = 2;
- var pos = Index(row, col);
- while (true) {
- var jet = jets[(jetSource..moveNext()).current];
- // blow
- if (canMove(rocks[rock]!, pos + jet)) pos += jet;
- // fall
- if (!(canMove(rocks[rock]!, pos + fall))) {
- break;
- }
- pos += fall;
- }
- topRow = [topRow, rocks[rock]!.map((e) => e.r).max + pos.r].max;
- rocks[rock]!.forEach((e) => grid[e + pos] = '#');
- if (!shortCutRun && jetSource.current == 0 && topRow > nLinesToCheck) {
- var key = (topRow - nLinesToCheck)
- .to(topRow)
- .map((e) => grid.row(e).join(''))
- .join('')
- .hashCode;
- if (seen.containsKey(key)) {
- //we have a repeat
- shortCutRun = true;
- // printIt(grid, Index(topRow, 0));
- // print('repeat ${seen[key]} -- $cycle $rockCount $topRow');
- // print('');
- var rDiff = rockCount - seen[key]!.first;
- var topDiff = topRow - seen[key]!.last;
- var mult = (targetRock - rockCount) ~/ rDiff;
- rockCount += rDiff * mult;
- topAdd += topDiff * mult;
- } else {
- seen[key] = [rockCount, topRow];
- }
- }
- rockCount += 1;
- if (rockCount >= targetRock) break;
- }
- return topRow + 1 + topAdd;
- }
- printIt(ListGrid grid, Index pos, {shape = const <Index>[]}) {
- for (var r in 0.to(pos.r + 5).reversed) {
- var p = '';
- for (var c in 0.to(grid.width)) {
- p += (shape.contains(Index(r, c) - pos)) ? '@' : grid[Index(r, c)];
- }
- print('${r.toString().padLeft(4)}: #$p#');
- }
- print(' ${'#' * (width + 2)}');
- print('');
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement