--- Day 17: Tetris ---

December 17

Listening to: 𝙈𝙪𝙨𝙞𝙘 𝙛𝙤𝙧 𝙀𝙙𝙞𝙩𝙞𝙣𝙜 𝘾𝙂𝙄 𝙤𝙣 𝙖 𝙁𝙧𝙞𝙙𝙖𝙮 𝙉𝙞𝙜𝙝𝙩


Tetris. It's Tetris. It's just friggin Tetris.


Okay. But for real, this doesn't seem too bad. Simulate falling rocks. It sounds like an extention of day 14, but with an NxM object instead of just a point.

I think as an easier way of doing this, I'm going to implement, in order:

  • Pieces falling
  • Pieces stopping when they hit the bottom
  • Pieces stopping when they hit another piece
  • Generate more space
  • Pieces being affected by the jet streams

So, in order to get pieces falling, just lower them by one, and test the piece's definition against that section of the board

int row = room.size()-piece.size(), col = START_COL;   
// Piece falling
while (row > 0)
{
    // Test below
    bool collision = false;
    for (int i = 0; i < bottom.size(); i++)
    {
        if (bottom[i] == '.') continue;
        if (room[row-1][col+i] == '#') { collision = true; break; }
    }

    if (collision) break;
    row--;
}

Then alternate that with moving it via an air jet

// Move via jet
bool collide = false;
int dir = 0;
switch (jets[jetIndex++])
{
    case '<': 
        if (col-1 >= 0) dir = -1;
        break;

    case '>':
        if (col+bottom.size() < room[0].size()) dir = 1;
        break;
}
jetIndex %= jets.size();
if (dir != 0)
{
    for (int r = 0; r < piece.size(); r++)
    for (int c = 0; c < bottom.size(); c++)
        if (piece[r][c] == '#' && room[row+r][col+c+dir] == '#')
            collide = true;

    if (!collide) col += dir;
}

And that's it! Just commit that piece to that location in the board, do it for however many iterations, and that's that!

... except for the buggy collision code I have for falling. It worked with pieces just falling, but now that they can kind of bump into each other weirdly, just replace that initial 1d for-loop with something similar from the 2d for loop in the jet movement.

bool collision = false;
for (int r = 0; r < piece.size(); r++)
for (int c = 0; c < bottom.size(); c++)
    if (piece[r][c] == '#' && room[row+r-1][col+c] == '#')
        collision = true;

Ugh. Part 2 has 1,000,000,000,000 iterations now. Basically impossible to simulate reasonably.

Okay, the pieces have to go in a regular cycle. Eventually. I just need to figure out when that cycle begins, and what that looks like. Keep a pair of nextPiece and jetIndex, and keep going until I see it again? Slip some code in temporarily just to test it out:

if (!inCycle && !seenCombo.emplace(pair<int, int>(nextPiece, jetIndex)).second)
{
    inCycle = true;
    cycle = pair<int, int>(nextPiece, jetIndex);
    cout << "FOUND AN ITERATION" << endl;
}

if (inCycle && nextPiece == cycle.first && jetIndex == cycle.second)
{
    cout << iteration << "\t: " << (room.size() - pastResult) - NEW_SPACE << endl;
    pastResult = room.size();
}

Running it on the sample data gives me:

FOUND AN ITERATION
50      : 78
85      : 53
120     : 53
155     : 53
190     : 53
225     : 53
260     : 53
295     : 53
330     : 53
365     : 53
...
11320   : 53

Okay, that's pretty clear! It looks like starting at rock 50, and every 35 cycles, the height goes up by 53! I just need to do math once I've identified that cycle to figure out! (And yes, it does work with my real input too, just with much larger numbers). Now to harden the code into something more reasonable!

// Full cycle found, start computing end state!!
if (inCycle && !finishing && nextPiece == cycle.first && jetIndex == cycle.second)
{
    uint64_t cycleHeight = room.size() - pastResult;
    uint64_t cycleLength = cycleStart - remaining;
    height = room.size() - NEW_SPACE;
    cout << "Cycle: [" << cycleHeight << "," << cycleLength << "] " << remaining << endl;

    height += cycleHeight * (remaining/cycleLength);
    remaining %= cycleLength;
    finishing = true;

    remaining += 1;
    continue;
}

// Mark known cycle
if (!inCycle && !seenCombo.emplace(pair<int, int>(nextPiece, jetIndex)).second)
{
    inCycle = true;
    cycle = pair<int, int>(nextPiece, jetIndex);
    pastResult = room.size();
    cycleStart = remaining;
    cout << "FOUND AN ITERATION" << endl;
}

This one absolutely took me a lot longer than I expected. I can't say why, it was mostly just the initial stages that caught me off guard. But it's late enough that I'd expect them to be this difficult. I'm not sure I can finish by the end of Christmas Eve like I had hoped, if they keep taking as long as they are.


Time taken: 4 hours

My solutions for today's puzzles