Tetris for Intro/Intermediate Programmers
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.  Also, for testing purposes, we will add temporary code that changes the falling piece whenever any other key is pressed.  This code will be removed after this step.

• 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 wind up 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 there 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 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.

• Explaining moveFallingPiece:
Next, we will write a function that will move the falling piece a given number of rows and columns:
def moveFallingPiece(canvas, drow, 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.  Consider:  a move to the left would change our column by -1 (since columns go down to the left), and keep our row constant.  Thus, with this function, we can move to the left by calling moveFallingPiece(canvas,0,-1).  Similarly, we can move to the right with moveFallingPiece(canvas,0,+1).  And to move down, we hold our column constant and add one to our row:  moveFallingPiece(canvas,+1,0).

• Writing moveFallingPiece:
As noted above, we proceed in three steps.  First, we simply make the move by modifying the canvas's data 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, and 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 canvas's data values to their original values prior to the call to moveFallingPiece.

• Explaining fallingPieceIsLegal:
This function is similar to drawFallingPiece.  It 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), rather than draw the cell (as drawFallingPiece does), this method 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 cell in the fallingPiece, the function returns True.

• Writing keyPressed:
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 (and to change the falling piece in response to any other key press for now).

 David Kosbie Carnegie Mellon University