The goal for this step is to select a random falling piece
in a random color, to position it in the top-middle of the board, and draw
it over the board. Note that the "falling piece" will not be falling
after this step. That comes later. For now, it will remain at
the top-middle of the board. Also, for testing purposes, we will add
temporary code that changes the falling piece whenever any key is pressed.
This code will be removed after this step.
Recall
that the falling piece is not part of the board, but drawn over the
board. It becomes part of the board when it can no longer fall
and we place it on the board, which will not happen until several steps
from now in our design process.
Defining the piece types:
In this design, the falling piece is represented by a 2-dimensional list of
booleans, indicating whether the given cell is or is not painted in this
piece. For example, here is the definition of an S piece:
sPiece = [
[ False, True, True],
[ True, True, False ]
]
This defines a 2-dimensional boolean list with 2 rows and 3 columns.
The "True" values have been highlighted to make it clear that they form an
S
pattern. This is how we know which cells are
part of the falling piece.
Here are all the piece types, with the "True" values highlighted to make the shapes easier to discern (note that they are provided in their standard configurations -- how they should enter the board from the top -- so, for example, the T piece is upside down):
#Seven "standard" pieces (tetrominoes)
iPiece = [
[ True, True, True, True]
]
jPiece = [
[ True, False, False ],
[ True, True, True]
]
lPiece = [
[ False, False, True],
[ True, True, True]
]
oPiece = [
[ True, True],
[ True, True]
]
sPiece = [
[ False, True, True],
[ True, True, False ]
]
tPiece = [
[ False, True, False ],
[ True, True, True]
]
zPiece = [
[ True, True, False ],
[ False, True, True]
]
For this design, we need to place all 7 of these piece types
into a single list, tetrisPieces. That will be a list of
2-dimensional lists, so it is in fact a 3-dimensional list! Here it
is:
tetrisPieces = [ iPiece, jPiece, lPiece, oPiece, sPiece, tPiece, zPiece ]
We also need to define colors corresponding to each of these
pieces, and place them in a list of the same size, which we do as such:
tetrisPieceColors = [ "red", "yellow", "magenta", "pink", "cyan", "green", "orange" ]
Store these values in the data fields:
We
should define the pieces and the piece colors in our init function, and
then we should also store them in the data object, with this code:
data.tetrisPieces = tetrisPieces
data.tetrisPieceColors = tetrisPieceColors
Writing newFallingPiece:
The newFallingPiece function (which takes one parameter, data, and returns nothing) is responsible for randomly choosing a new
piece, setting its color, and positioning it in the middle of the top
row. The first step is to randomly choose an index from the
tetrisPieces list (using the random.choice or random.randint function), then to set the
data values holding the fallingPiece and the fallingPieceColor
to the respective elements from the lists of tetrisPieces and
tetrisPieceColors.
Next, we set the top row of the falling piece (fallingPieceRow) to the
top row of the board (that is, to zero). And then we set the left column of the new falling piece
(fallingPieceCol). As we want
the piece to emerge at the top-middle of the board, we might set this to the
center, or cols/2. However, this will place the new falling piece
a bit off to the right, since its full width would extend beyond the middle.
To compensate for this fact, we subtract half the width of the falling
piece, or fallingPieceCols/2, from the middle of the board
Drawing the falling piece:
After calling drawBoard, you should then call drawFallingPiece, so the falling piece is drawn over the board
(in this way, to the user it looks like the falling piece is on the
board, but in reality it is stored separately and drawn
separately). To draw the falling piece (in the drawFallingPiece
function, which takes both canvas and data), we iterate over each cell in the fallingPiece, and if the
value of that cell is True, then we should draw it reusing the
same drawCell function that drawBoard uses, but in the color of the
fallingPiece (rather than the color stored in the board for that row
and column). However, we have to add the offset of the left-top
row and column (that is, fallingPieceRow and fallingPieceCol)
so that the fallingPiece is properly positioned on the board.
Also, note that this step requires that we add an additional parameter
to the drawCell function -- the color to draw the cell.
Initial falling piece:
We must add one line to our init function to select the first falling piece of
the game (by calling the newFallingPiece function we just wrote).
Temporary test code:
As noted above, for testing purposes, we will add temporary code that
changes the falling piece whenever any key is pressed. This code will
be removed after this step. This involves the following simple key
event handler:
def tetrisKeyFn(event, data):
# for now, for testing purposes, just choose a new falling piece
# whenever ANY key is pressed!
newFallingPiece(data)
Don't forget to add keyFn=tetrisKeyFn to your call to eventBasedAnimation.run().
David Kosbie |