Tetris for Intro/Intermediate Programmers (CS Scholars update)
Step 6: Dropping and Placing the fallingPiece on the Board and Handling Game-Over
We are now ready to have the falling piece respond to time events by dropping one row on each call to timerFired. To start, we can have our timerFired function call moveFallingPiece(data, +1, 0), just as we do in response to a down-arrow key press. This is not a bad start, but when it gets to the bottom, it just sits there (until we hit a non-arrow key to artificially reset the falling piece).
To remedy this, we first need to know when the piece stops moving! That requires a small change to some code we have written. We will change moveFallingPiece so that now it returns True if the move occurred, and False if the move failed for any reason (the piece moved off the board, or the piece collided with something on the board). This requires only a small change to our code.
Once we have done this, our timerFired can test the return value from moveFallingPiece to determine if the move failed. In that case, we will use top-down design and call a function (that we are about to write) to place the falling piece on the board.
Hint: Remember that we only want to place the piece when timerFired tries to move the piece and fails. We should not place the piece when the user tries to make an illegal move, for example, or else we might get pieces sticking to the side of the board or frozen in odd positions.
Writing the placeFallingPiece() function:
This function (which only takes data, and not canvas) is quite similar to drawFallingPiece, only rather than draw the cells, we load the corresponding cells of the fallingPiece onto the board with the fallingPieceColor. In this way, the piece is placed on the board.
Finally, add a line right after the call to placeFallingPiece in timerFired that uses newFallingPiece to start a new piece falling from the top. Once you get this working, our game springs to life! But now we see another problem: the game never ends. The pieces pile up to the top, but then they just keep pouring out. We should fix this!
When should the game end? When we place a falling piece and it is immediately illegal. We can test for this in our timerFired, right after we call newFallingPiece. There we can create a new data value "isGameOver" (which is set to False in init) and set it to True once we reach our game-over state.
But what should we do when the game has ended? We will update our keyPressed and timerFired functions to check for this variable, and stop all game interaction once the game is over.
Writing the drawGameOver() function: Also, if the variable is True, the draw function should draw a suitable message stating that the game is over. You should put the code to draw that message in a new function drawGameOver(canvas, data) just to keep your code organized.
Finally, we will let the user start a new game at any time by hitting 'r' for 'restart', and calling the init function in response. Be sure your keyPressed continues to look for the 'r' key, even the the game is over, or else it will be impossible to restart!
At this time we will also remove our test code from prior steps, both the call to newFallingPiece in response to a non-arrow key press and the four corners in init.
Note: it can be hard to test the game when the pieces move fast. To make your pieces move slower, change the time rate in the call to runSimulation in playTetris to be a larger float (for example, 0.5 will make the piece only move two steps per second).
At the end of this stage, your Tetris board should be able to do this: