Tetris for Intro/Intermediate Programmers (Spring 2020 version)
Step 4: Moving the fallingPiece left/right/down
In this step, we have our falling piece respond to left-arrow, right-arrow, and down-arrow key presses by moving in the given direction. Actually, we only want to move in a given direction if it is legal to do so. There are two reasons why it may not be legal: the falling piece may move off of the board, or a part of the falling piece may collide with a non-empty cell on the board. In either case, we should not move the falling piece.
Among other consequences, this means that for now, once pieces reach the bottom of the board, we will not be able to move them any lower, yet we will not place them on the board. That comes later. For now, the piece will stay at the bottom until we use the temporary test code to change the falling piece (which also places the new falling piece back at the top-center of the board).
One approach for moving the falling piece would be to test if a move is legal first, and if it is, then make the move. That approach requires different code, however, to test for each type of move we might make (left, right, down, and, eventually, rotation), which is unnecessarily complicated. An easier approach is to blindly make the move, then test if the result of the move is legal. If it is not legal, then unmake the move. In this way, a single legal move test can be used for any sort of move. We will use this move-test-unmove design.
Writing the moveFallingPiece() function:
Now we will actually write the function moveFallingPiece, which will move the falling piece a given number of rows and columns. This function will take the app object along with two other parameters, drow and dcol. We use "drow" to signify a change in rows (the "d" in "drow" stands for the mathematical symbol "delta", meaning change). Similarly, "dcol" is the change in columns.
For example: a move to the left would change our column by -1 (since columns decrease when moving to the left) and keep our row constant. Thus, with this function, we can move to the left by calling moveFallingPiece(app, 0, -1). Similarly, we can move to the right with moveFallingPiece(app, 0, +1). And to move down, we hold our column constant and add one to our row: moveFallingPiece(app, +1, 0).
To actually move the falling piece, we proceed in the three steps described above. First, we simply make the move by modifying the app values storing the location of the left-top corner of the falling piece. Next, we test if this new location of the falling piece is legal. We do this using top-down design, so we assume the function fallingPieceIsLegal exists at this point (we'll actually write it in a moment). If the new location is not legal (because it was off the board or because it collided with a non-empty cell on the board), then we undo the move we just made by resetting the app values to their original values prior to the call to moveFallingPiece.
Writing the fallingPieceIsLegal() function:
This function iterates over every cell (every row and every column) in the fallingPiece, and for those cells which are part of the falling piece (that is, where the falling piece list has a True value), it confirms that: (1) the cell is in fact on the board; and (2) the color at that location on the board is the emptyColor. If either of these checks fails, the function immediately returns False. If all the checks succeed for every True cell in the fallingPiece, the function returns True.
Finally, we modify the keyPressed handler to call moveFallingPiece to move left, right, or down in response to left-arrow, right-arrow, or down-arrow key presses. We can continue to generate a new falling piece in response to any other key press for now.
If you are debugging this step, remember that sometimes print statements can help you debug what your code is doing. Just remember to remove them when you're done for good style!
At the end of this stage, your Tetris board should be able to do this:
David Kosbie
Carnegie Mellon University koz@cmu.edu