Our game is close to being complete -- or at least complete
enough that it can be fun to play. All that remains is removing full
rows (since filling rows is the object of Tetris) and keeping score.
Writing removeFullRows:
This function will iterate a variable, oldRow, from the bottom to the top
of the board (that is, from rows-1 to 0). It will also start
another variable, newRow, at the bottom of the board. Each oldRow that
is not full is copied to the newRow, which is then decremented by one.
Each oldRow that is full is not copied, and the fullRows counter is
incremented. At the end, the rows on top are cleared (set to
emptyColor) as necessary
Note that there is a very common mistake here: instead
of copying a row element-by-element, you might simply assign the oldRow into the newRow as such:
board[newRow] = board[oldRow] # WRONG!!!
While it is possible to make this approach work (and it can even be
more efficient than the approach described above), it is almost
universally a source of bugs and confusion. You will likely end
up with two or more rows in your board referencing the same list (that
is, they will be "aliases" rather than "copies")! This leads to
very confusing situations, but now you know to expect it and to avoid
it!
Now that we are removing rows, we should keep score.
To do that, we will introduce a canvas data value "score" (which is set to
zero in the init function). In our removeFullRows function, we will
increment the score by the square of total number of full rows
removed in order to reward removing
multiple lines at once. And now that we are keeping score, we should
display the score, which involves adding a function drawScore, which is
called by redrawAll.
[This
next step was relatively easy in the Java version of these notes, but in
Python it requires using modules or techniques we've not yet covered,
so we will not play a MIDI soundtrack in this version.]
We're basically done now, but seeing how easy it is at this
point (using JComponentWithEvents), we'll add a MIDI soundtrack for our game. This can be done with
two lines of code. First, at the end of resetGame, add:
loop("tetris.mid"); // play tetris.mid in a loop
Then, in the timerFired method, when it sets this.isGameOver to true,
add this line:
stopSounds();
Note that the file "tetris.mid" is
included with these notes.
David Kosbie |