Tetris for Intro/Intermediate Programmers (CS Scholars update)
Step 3: Creating and Drawing the fallingPiece

• The goal for this step is to select a random falling piece in a random color, 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 at a later point.

• Recall that the falling piece is not part of the board, but drawn over the board. Drawing the piece over the board just requires that we draw the board first, and the piece second when redrawAll is called (otherwise, if we draw the piece first, we won't be able to see it because the board will be drawn over it). Later, we will draw a piece as part of the board when it can no longer fall, which will not happen until several steps from now in our design process.

• First, let's define how we'll represent the pieces. 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. Below we list 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. That will be a list of 2-dimensional lists, so it is in fact a 3-dimensional list! (Don't think of this in terms of 3D space, however... it's only 3D in the sense that it is a list of lists of lists.) 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" ]

• We've added a function getTetrisPieces() to the starter file that returns the list of tetris pieces for you. Call this function and use the tetrisPieceColors line above to define the pieces and the piece colors in your init function, and store them in the data object. This will then prepare you to write the function to generate a new falling piece.

• Writing the newFallingPiece() function:
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. You can do this with random.randint!

• Then we set the data values holding the fallingPiece and the fallingPieceColor to the randomIndex-ed elements from the lists of tetrisPieces and tetrisPieceColors.

• To complete our representation of the fallingPiece, we need to store its position relative to the board. We set the top row of the falling piece (fallingPieceRow) to the top row of the board (that is, to zero). Then we set the left column of the new falling piece (fallingPieceCol) so that it will be placed in the center of the columns. We might set this to the center, or cols//2, but 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 numFallingPieceCols//2, from the index of the middle of the board.

• Note that we can't exactly center some pieces, e.g. we can't center a three-wide piece in a ten-wide board. Odd-width pieces can't be perfectly centered on an even-width board, and even-width pieces can't be centered on an odd-width board. Instead we get as close as we can with integer divide (//). The initial orientation for pieces can be two, three, or four cells wide, so we will encounter this for both even- and odd-width boards. See the gif below to see how different pieces are centered on an even-width board.

• Writing the drawFallingPiece() function:
After calling drawBoard, you should then call a new function drawFallingPiece (which takes canvas and data), so that 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 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).

• Note that 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 fill the cell. Make sure to update how you called drawCell in drawBoard as well!

• Finally, to actually generate a 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). At this point, for testing purposes, we will add temporary code that changes the falling piece whenever any key is pressed. To do this, we simply need to call newFallingPiece in our keyPressed function.

• At the end of this stage, your Tetris board should be able to exhibit the behavior shown in the gif below. If not, stop and figure out why before proceeding! (This is a good strategy for each page of this problem, but this is the last time we'll remind you to debug as you go.)

 David Kosbie Carnegie Mellon University koz@cmu.edu