## CS 101 (Spring 1999) Lab 11: The Ultimate Tetris Lab

Lab Assigned Design Due
(Mondays 2 PM)
Lab Due
(Fridays 2 PM)
16 Apr None 26 Apr (No late coupon)

### Overview:

Tetris: (click the name to play somebody else's implementation)
• Tetris shapes: They're rough, rectilinear, and ready to rotate.
• You're one lab away from finishing this course.
• Need I say more?

### Goals:

By the end of this lab, you should
• Take a much needed rest -- go ahead, take the whole Summer off.
• Have a rudimentary implementation of a Tetris-like game.
Most labs contains two parts, design and implementation, due on the dates shown above by 2 PM. This lab doesn't. The design is provided in this document.

### Before starting:

• Read over this entire document before you start.
• Run the sample solution (Run under Internet Explorer).

Zip includes:

• Files you are given but you shouldn't modify:
• Files you are given that you must modify:

Remember to type the following lines at the top of any files that use the terminal or canvas classes.

```import cs101.terminal.*;
import cs101.canvas.*;
```

### Particulars

The design of this lab is somewhat different than Lab 10. The notion of a Side is gone. The reason is that the tetris pieces will occupy space on a grid-like canvas. It is therefore the area of the squares that count, and not the inducing vector.

Thus, pieces are constructed differently than before. For examples, take a look at TetrisGamma and its antimatter twin TetrisAmmag. You will have to construct the other Tetris pieces yourself.

This document describes the design without telling you the coding details. That is, the overall structure of the solution is given, but actual code is provided by you.

### What makes a tetris piece?

It is helpful to think of a tetris shape as if it were constructed of squares that are glued together:

 Above, you see a tetris piece (the Gamma piece, named for the shape of the Greek letter) in outline form, as you would see the piece when you play. Above, you see how the piece is constructed using four square blocks. Check out the constructor in `TetrisGamma.java`. You see that one of the square blocks is distinguished. It is the main component, an instance of `TetrisPiece`, which is a special kind of `TetrisBlock`. In other words, `TetrisPiece` extends `TetrisBlock`.

A Tetris piece is constructed by gluing `TetrisBlock`s together. When block B is glued to block A, block B must follow block A around, wherever A might go. When block B is glued to block A, block B is told its offset, grid-wise, from block A.

When a `TetrisBlock` moves around, so must the blocks glued to it. The blocks glued to it are listeners. Since a block has four sides, it can have at most four listeners. You will keep track of a block's listeners using an array .

A block is moved by calling its `moved` method. That block, in turn, must call its listeners' `moved` methods.

A block is rotated by calling its `rotated` method. That block, in turn, must call its listeners' `rotated` methods.

### The APIs:

`CS101CanvasGrid`

To ease the placement of tetris shapes, you will extend `CS101Canvas` to `CS101CanvasGrid`, so that the space can be regarded as a grid of rectangles, each of which is occupied or not occupied.

Additionally, this kind of canvas is setup so that it can listen to key presses and call methods based on them. The class can register an `ArrowListener`, so that methods of the listener are called based on key presses as follows:

 On some computers, for some windowing systems, the arrow keypresses may not be transmitted. Thus, each gesture is duplicated by a text key. The keys are chosen to mimic the intended direction, based on the number keys' arrangement on the numeric keypad of most keyboards. Key Press Method Called 2 Down Arrow down() 4 Left Arrow left() 6 Right Arrow right() 8 Up Arrow rotate()

Since `TetrisGame` controls the game, it will want to register itself as the `ArrowListener` of its `CS101CanvasGrid`.

Here is the API:

`CS101CanvasGrid(int nc, int nr)`
The constructor takes in the number of columns and rows. The superclass constructor is called to initialize a `CS101Canvas`.

Then, a grid (two-dimensional array) of `GridSquare`s is set up. Initially, all grid locations are unoccupied except those at the extreme left, right, and bottom of the canvas.

`setOccupied(int i, int j, boolean val)`
establishes that the grid location at column `i` and row `j` is occupied (if `val` is true) or unoccupied (if `val` is false).
`boolean isOccupied(int i, int j)`
returns the occupied status of the grid location at column `i` and row `j`.
`GridSquare`
This class extends `Rect` so that a rectangle can remember whether it is occupied or not. In the extension, the following methods must be provided:
`GridSquare(int col, int row, int squaresize)`
The constructor calls its superconstructor to establish a `Rect` at the desired column and row. Remember to convert from column and row to pixel coordinates, using the supplied square size.
`public void setOccupied(boolean val)`
sets the occupation status to the supplied parameter.
`public boolean isOccupied()`
returns the occupation status of the rectangle.
When the rectangle becomes occupied, it sets its color to `Color.red`. When the rectangle is unoccupied, it sets its color to `Color.white`.
`Startup`
Contains the usual testing calls, and then instantiates a `TetrisGame`. You will need to add `selfTest` calls for any classes you add.
`TetrisGame`
Constructs a tetris game and repeatedly places pieces and listens for user-driven instructions to move the piece. In this class, `curPiece` is the piece currently being manipulated on the screen.
`TetrisGame(int cols, int rows, int pieces)`
constructs a Tetris game with the specified number of columns and rows on the grid. The `pieces` parameter could be used to limit the number of pieces offered in a game. If your game continues indefinitely, you may choose to ignore this parameter.
`TetrisPiece genPiece()`
is responsible for generating the next Tetris piece for the game. Use `Math.random()` so that the pieces are randomly and uniformly distributed.

Note that polymorphism allows a particular shape (`TetrisGamma`, `TetrisTee`, etc.) to be treated as the generic `TetrisPiece`.

`void advance()`
is called whenever a motion has been performed on the piece. This method ensures progress of the game by every so often (1 in 3 times as the code is given to you) making the pieces move down one square.

If we used Java threads and concurrency, we could advance the piece down the screen automatically even if the user didn't do anything. This is what a real game would do, and you will learn about concurrency in CS 102.

Because `TetrisGame` is an `ArrowListener`, it must implement the methods in that interface:
`void left()`
The left arrow (or its equivalent number key) is pressed.
`void right()`
The right arrow (or its equivalent number key) is pressed.
`void down()`
The down arrow (or its equivalent number key) is pressed.
`void up()`
The up arrow (or its equivalent number key) is pressed.
`TetrisBlock`
This class represents a block (square) that can be part of a Tetris piece. You have to set up the block as a listener to its inducing side. You also have to place the block's four sides, given the analysis in this document.
`TetrisBlock(leader, canvas, dX, dY)`
constructs a TetrisBlock that is in relationship to `leader`, offset in grid positions by `dX` and `dy`.
`addListener(TetrisBlock listener)`
adds a listener (a TetrisBlock) to this TetrisBlock. There can be at most 4 listeners. You must implement the listeners as an array.

For each of the following methods, once "this" TetrisBlock has performed its intended action, the block should invoke the same method on each of its listeners.

`rotated()`
is called when this piece is to be rotated counterclockwise from its current relationship with its leader. Its `dX` and `dY` must be adjusted, as discussed in class.
`moved()`
is called when this block is possibly moved. The block must determine its new column an row coordinates by adding `dX` and `dY` to its leader's coordinates.
`allClear()`
is called to determine if
• the Tetris board is unoccupied at this block's current coordinates, and
• all listeners of this block are similarly `allClear`.
`pickUp()`
If the piece is down, picks up the piece by informing the canvas that its grid location is unoccupied.
`putDown()`
If the piece is up, puts down the piece by informing the canvas that its grid location is occupied.
`TetrisPiece`
is a special kind of `TetrisBlock`.
`TetrisPiece(CS101CanvasGrid canvas, int col, int row)`
calls the superconstructor with `dX` and `dY` of 0. The column and row for "this" block is then established as the parameters' values.
`boolean proposeMove(int newCol, int newRow, int oldCol, int oldRow)`
This is an important method. It proposes a move to the new parameter values. The following sequence of actions should occur:
1. The piece is picked up.
2. The piece is moved.
3. It is determined if the board is `allClear` at the new location.
• If so, the piece is put down.
• If not, the piece is moved back to its old location and put down.
4. The method returns true if the move was successful; otherwise, false is returned.
Because of the way listeners are set up, the actions on "this" `TetrisPiece` (and therefore, on "this" `TetrisBlock`) are propagated to all blocks making up this piece.

`exercise`
This exercises a piece, moving it, rotating it, and then randomly applying those transformations.
`abstract String name()`
This method makes this class abstract. Each subclasses must provide a name for its piece.

Each of the following actions, called from `TetrisGame`, can be reduced to a call to `proposeMove`. Each returns true if the move is successful; otherwise, false is returned.

`boolean moveLeft()`
Try to move this piece left (negative x direction) one square.
`boolean moveRight()`
Try to move this piece right (positive x direction) one square.
`boolean moveUp()`
Try to move this piece up (negative y direction) one square.
`boolean moveDown()`
Try to move this piece down (positive y direction) one square.

Rotation requires a little more work. We have to try the rotation, and if it doesn't work out, then we have to unrotate the piece. Hint: You can unrotate a piece by rotating it three more times counterclockwise.

`boolean rotateCounterClockwise()`
The following sequence of steps is performed:
1. The piece is picked up.
2. The piece is rotated. This updates the blocks' `dX` and `dY` values.
3. The piece is informed that it has moved, so as to recompute the blocks' locations.
4. It is determined if the board is `allClear` at the new location.
• If so, the piece is put down.
• If not, the piece is unrotated and put down.
5. The method returns true if the move was successful; otherwise, false is returned.
`boolean rotateClockwise()`
Can be accomplished by multiple calls to `rotateCounterClockwise`.

Each of the following objects extends `TetrisPIece.java` to obtain a particular kind of Tetris piece.

`TetrisSquare`
The four-square Tetris square.
`TetrisGamma`
The Gamma piece, shown in this document.
`TetrisAmmag`
The mirror image of `TetrisGamma`
`TetrisTee`
Three blocks in a row, with one block below in the middle.
`TetrisZigZag`
The piece described in Lab 10.
`TetrisGazGiz`
The mirror image of `TetrisZigZag`
`TetrisStraight`
Four blocks in a row.

### What to turn in:

Implementation
1. Complete a code cover sheet.
2. Provide a transcript from your self-tests.
3. Complete the classes as described above.
4. Provide a printout of any files you have modified.
5. Be prepared to demo in lab.