The document provides instructions for completing an assignment to add additional pieces and rotation functionality to an existing Tetris game coded in Java. It describes refactoring the code into a superclass and subclasses for each piece type. It then details how to initialize and add code for the remaining 6 piece types. It also explains how to implement rotation by checking if moves are valid and using dynamic dispatch. The student is asked to test each new piece individually, follow object-oriented design principles, and write a report discussing their process.
1. Hi there I am having difficulty in finalizing my Tetris game , below I posted the codes for the
classes and I need the rest six pieces to be done. Can anybody help me here. It is a java code and
must use the code I posted bellow. Thanks,
Completing Tetris
[Description: shapes]
Overview
You will be completing the game Tetris. The features needed are
1. Adding the other 6 pieces
2. Adding the ability to rotate the pieces
Concepts
The purpose of this assignment is to gain experience with the following new concepts:
inheritance/class hierarchy
dynamic dispatch
algorithms
If you were not able to complete homework 1, please come see me to get a working solution.
Implementation Requirements
OTHER PIECES: The first step in the assignment is to add the other six pieces. Lets think a little
about the planning though; we don't want the Game class to have to know about all 7 different
piece types (we wouldn't want Game to have 7 different instance variables, 1 for each type. How
would it keep track of which one was current?) Instead, Game should know about 1 type (a super
type) and let dynamic dispatch do the work for us.
So to start, we need to redesign (or refactor) the current code. To do this, we want a super class
that contains everything common to all pieces; then sub classes for the individual pieces and
their individual needs. So what is the same about all the pieces?
the state currently handled by LPiece is common to all pieces
all the behaviors currently in LPiece are common to all pieces (except the constructor of course)
What is different about each piece:
How each individual playing piece is constructed
The implementation of how each rotates (the second feature for this assignment)
1) Therefore, start by breaking up the LPiece.java class into a super class and sub class. At this
point, test your program. It should run as it did before.
2) Now its time to add the other game pieces. You will need to figure out how to initialize the
pieces. This will be similar to how the L-shaped piece was done, and, in fact, you may find it
helpful to start each new class by copying the code from a previous shape and modifying it. The
pieces should be initialized in the following orientations:
2. [Description: shapes]
In the Game class, the piece instance variable must have a new type. What do you think it should
be?
You'll need to modify the Game class so that it doesn't always instantiate an LPiece piece, but
randomly chooses which shape to instantiate. (I strongly recommend creating 1 new shape at a
time and testing.)
ROTATION: The current tetris piece needs to be able to rotate in quarter turns clockwise each
time the 'r' key is pressed. As an example see the figure below illustrating how the L-shaped
piece rotates:
[Description: Lrotate]
Here is where dynamic dispatch will come in handy since each shape class must have its own
rotate implementation.
Update the existing classes (super and sub classes) to enable dynamic dispatch to work properly.
Modify the EventController class to react to the 'r' key.
Rules for Rotation: A piece should only rotate if
the grid squares that it would pass through during rotation are empty, and
the grid squares that it would occupy after rotating are all empty, and
all the squares are within the boundaries of the grid as the piece rotates.
Notice in the figure below how the different squares which make up the piece move their
positions. The figure shows the surrounding grid so you can see more clearly where the
movement happens. In particular, square 2 doesn't move at all, and 0,1,3 move relative to it.
Square 1 must "move through" the upper left square (row 0, col 0) before it stops in its final
location (row 0, col 1)
[Description: sampleRotation]
Square 2 is chosen as the pivot Square because it is used as the "base" square when the LPiece
is constructed. If you look at the constructor for the LPiece, the (r, c) parameters define the
position of the middle square on the long side. That is the square rotated around. For each shape,
you need to determine which square is closest to the "middle" and rotate around that.
For most pieces, the square to rotate around is the one establish by the (r,c) parameters. For the
square shape, though, it makes sense to consider the geometric center as the center of rotation
instead. This makes the rotation a trivial solution. (This is not true for the straight piece).
If a piece is rotated 4 times, it should be back in its original orientation and its original location
3. on the grid (assuming no other movement). In other words, no "drifting" over the grid.
Here's an example of the sort of thing that boundary checking on rotation should prevent:
[Description: boundary]
The other consideration when implementing this feature is "how to check if the rotate is legal?
Are the appropriate squares on the grid empty?" As we've discussed in class, implementation is
left as a developer's choice, but there are some guidelines you must follow:
Notice that in the move() method of a piece, the piece queries the individual squares if they can
move, and then tells them to move. At no time does the piece ever query the Grid directly.
Implement rotate the same way. Find out if the individual squares can move as needed, then
move them. You may add more methods to the Square public interface. In some rotations, a
Square actually has to move up. You may want to add an UP constant (to the appropriate class)
and modify the appropriate methods/comments in the Square class accordingly.
Written Report
Planning: How did you plan and organize your code?
Testing: How did you test your code? What sort of bugs did you encounter? Are there any
unresolved problems in the code? Be sure to list any ways in which the program falls short of the
specification.
Evaluate this project. What did you learn from it, or what did it help you remember? Was it
worth the effort? This could include things you learned about specifications and interfaces,
design issues, Java language issues, debugging, etc.
Please bring a printout of your written report to class the day after the due date.
Grading
/10 execution (does it work properly)
/4 design
/4 documentation (both internal and external), style
/2 written report
The codes are:
import java.awt.*;
/**
* Manages the game Tetris. Keeps track of the current piece and the grid.
* Updates the display whenever the state of the game has changed.
*
* @author Anthony Bladek
* @version 01/23/2017
4. */
public class Game
{
private Grid theGrid; // the grid that makes up the Tetris board
private Tetris theDisplay; // the visual for the Tetris game
private LPiece piece; // the current piece that is dropping
private boolean isOver; // has the game finished?
// possible move directions
public static final int LEFT = 0;
public static final int RIGHT = 1;
public static final int DOWN = 2;
/**
* Create a Tetris game
* @param Tetris the display
*/
public Game(Tetris display)
{
theGrid = new Grid();
theDisplay = display;
piece = new LPiece(1, Grid.WIDTH/2 -1, theGrid);
isOver = false;
}
/** Draw the current state of the game
* @param g the Graphics context on which to draw
*/
public void draw(Graphics g)
{
theGrid.draw(g);
if (piece != null)
piece.draw(g);
}
/** Move the piece in the given direction
5. * @param the direction to move
* @throws IllegalArgumentException if direction is not legal
*/
public void movePiece(int direction){
if (piece != null)
piece.move(direction);
updatePiece();
theDisplay.update();
theGrid.checkRows();
}
/**
* Returns true if the game is over
*/
public boolean isGameOver() {
// game is over if the piece occupies the same space as some non-empty
// part of the grid. Usually happens when a new piece is made
if (piece == null)
return false;
// check if game is already over
if (isOver)
return true;
// check every part of the piece
Point[] p = piece.getLocations();
for (int i = 0; i = WIDTH || col < 0 || col >= HEIGHT
*/
public void set(int row, int col, Color c) {
board[row][col].setColor(c);
}
/**
* Check for and remove all solid rows of squares
* If a solid row is found and removed, all rows above
* it are moved down and the top row set to empty
6. */
public void checkRows() {
boolean rowFull = true;
int i = HEIGHT - 1;
while (i > 0)
{
rowFull = true;
for(int j = 0; j< WIDTH;j++)
{
if(!isSet(i,j))// colum is empty don't need to remove a row
{
rowFull = false;
i --; // only go to the next row if it is not full;
break;
}
}
if(rowFull)
{
System.out.println("Found a full row. row: " + i );
// redraw the grid with the every row above the one found drawn with the value above it.
shiftRows(i);
setRowEmpty(0);
}
}
}
/* Shift all the Rows above the input down one row
* private method so it does not have to have public comments
* input parameter is integer value of the row that was found to be full.
*/
private void shiftRows(int fullRow)
{
for(int m = fullRow; m > 1; m--)
{
for(int n = 0; n < WIDTH;n++)
7. {
set(m,n,board[m-1][n].getColor());
}
}
}
/*Set one row to empty
Input is the row to set to empty.
*/
private void setRowEmpty(int rowNum)
{
for(int n = 0;n < WIDTH; n++)
set(rowNum,n,EMPTY);
}
/**
* Draw the grid on the given Graphics context
* @param Graphics you are drawing with.
*/
public void draw(Graphics g){
// draw the edges as rectangles: left, right in blue then bottom in red
g.setColor(Color.blue);
g.fillRect(LEFT - BORDER, TOP, BORDER, HEIGHT * Square.HEIGHT);
g.fillRect(LEFT + WIDTH * Square.WIDTH, TOP, BORDER, HEIGHT * Square.HEIGHT);
g.setColor(Color.red);
g.fillRect(LEFT - BORDER, TOP + HEIGHT * Square.HEIGHT,
WIDTH * Square.WIDTH + 2 * BORDER, BORDER);
// draw all the squares in the grid
for (int r = 0; r < HEIGHT; r++)
for(int c = 0; c < WIDTH; c++)
board[r][c].draw(g);
}
}
import java.awt.*;
/**
8. * An LPiece item in the Tetris Game.
*
* This piece is made up of 4 squares in the following configuration
* Sq
* Sq
* Sq Sq
*
* The game piece "floats above" the Grid. The (row, col) coordinates
* are the location of the middle Square on the side within the Grid
*
* @author CSC 143
*/
public class LPiece
{
private boolean ableToMove; // can this piece move
private Square[] square; // the Squares that make up this piece
// Made up of PIECE_COUNT squares
private Grid theGrid; // the board this piece is on
// number of squares in 1 Tetris game piece
private static final int PIECE_COUNT = 4;
/**
* Create an L-Shape piece. See class description for actual location
* of r and c
* @param r row location for this piece
* @param c column location for this piece
* @param g the grid for this game piece
*
*/
public LPiece(int r, int c, Grid g)
{
9. theGrid = g;
square = new Square[PIECE_COUNT];
ableToMove = true;
// Create the squares
square[0] = new Square(g, r - 1, c, Color.magenta, true);
square[1] = new Square(g, r, c , Color.magenta, true);
square[2] = new Square(g, r + 1, c, Color.magenta, true);
square[3] = new Square(g, r + 1, c + 1, Color.magenta, true);
}
/**
* Draw the piece on the given Graphics context
*/
public void draw(Graphics g){
for (int i = 0; i < PIECE_COUNT; i++)
square[i].draw(g);
}
/**
* Move the piece if possible
* Freeze the piece if it cannot move down anymore
* @param direction the direction to move
* @throws IllegalArgumentException if direction is not Square.DOWN,
* Square.LEFT, or Square.RIGHT
*/
public void move(int direction){
if (canMove(direction)){
for (int i = 0; i < PIECE_COUNT; i++)
square[i].move(direction);
}
// if we couldn't move, see if because we're at the bottom
else if (direction == Game.DOWN){
ableToMove = false;
}
}
10. /** Return the (row,col) grid coordinates occupied by this Piece
* @return an Array of (row,col) Points
*/
public Point[] getLocations(){
Point[] points = new Point[PIECE_COUNT];
for(int i = 0; i < PIECE_COUNT; i++) {
points[i] = new Point(square[i].getRow(), square[i].getCol());
}
return points;
}
/**
* Return the color of this piece
*/
public Color getColor() {
// all squares of this piece have the same color
return square[0].getColor();
}
/**
* Returns if this piece can move in the given direction
* @throws IllegalArgumentException if direction is not Square.DOWN,
* Square.LEFT, or Square.RIGHT
*/
public boolean canMove(int direction) {
if (!ableToMove)
return false;
//Each square must be able to move in that direction
boolean answer = true;
for (int i = 0; i < PIECE_COUNT; i++) {
answer = answer && square[i].canMove(direction);
}
return answer;
}
11. }
import java.awt.*;
/**
* One Square on our Tetris Grid or one square in our Tetris game piece
*
* @author Anthony Bladek
* @version 01/23/2017
*/
public class Square
{
private Grid theGrid; // the environment where this Square is
private int row, col; // the grid location of this Square
private boolean ableToMove; // true if this Square can move
private Color color; // the color of this Square
// possible move directions are defined by the Game class
// dimensions of a Square
public static final int WIDTH = 20;
public static final int HEIGHT = 20;
/**
* Constructor for objects of class Square
* @param g the Grid for this Square
* @param row the row of this Square in the Grid
* @param col the column of this Square in the Grid
* @param c the Color of this Square
* @param mobile true if this Square can move
*
* @throws IllegalArgumentException if row and col not within the Grid
*/
public Square(Grid g, int row, int col, Color c, boolean mobile){
if (row < 0 || row > g.HEIGHT-1)
throw new IllegalArgumentException("Invalid row =" + row);
if (col < 0 || col > g.WIDTH-1)
throw new IllegalArgumentException("Invalid column = " + col);
12. // initialize instance variables
theGrid = g;
this.row = row;
this.col = col;
color = c;
ableToMove = mobile;
}
/**
* Return the row for this Square
*/
public int getRow() {
return row;
}
/**
* Return the column for this Square
*/
public int getCol() {
return col;
}
/**
* Return true if this Square can move 1 spot in direction d
* @param direction the direction to test for possible move
* @throws IllegalArgumentException if direction is not valid
*/
public boolean canMove(int direction){
if (!ableToMove)
return false;
boolean move = true;
// if the given direction is blocked, we can't move
// remember to check the edges of the grid
switch(direction) {
case Game.DOWN:
13. if (row == (theGrid.HEIGHT -1) || theGrid.isSet(row + 1, col))
move = false;
break;
case Game.LEFT: // check conditions for left motion.
// stop motion if off the grid or another block is in the way.
if ((col == 0) || (theGrid.isSet(row, col -1)))
move = false;
break;
case Game.RIGHT: // check the conditions for right motion
// stop the motion if off the grid or another block is in the way.
if(col == (theGrid.WIDTH -1 ) || theGrid.isSet(row, col + 1))
move = false;
break;
default:
throw new IllegalArgumentException("Bad direction to Square.canMove()");
}
return move;
}
/** move Square in the given direction if possible
* Square will not move if direction is blocked, or Square is unable to move
* If attempt to move DOWN and it can't, the Square is frozen
* and cannot move anymore
* @param direction the direction to move
*/
public void move(int direction) {
if (canMove(direction)) {
switch(direction) {
case Game.DOWN:
row = row + 1;
break;
case Game.LEFT:
col = col - 1;
break;
case Game.RIGHT:
col = col + 1;
14. }
}
}
/** Change the color of the Square
* @param c the new color
*/
public void setColor(Color c) {
color = c;
}
/** Get the color of this Square
*/
public Color getColor() {
return color;
}
/**
* Draw this square on the given Graphics context
*/
public void draw(Graphics g) {
// calculate the upper left (x,y) coordinate of this square
int actualX = theGrid.LEFT + col * WIDTH;
int actualY = theGrid.TOP + row * HEIGHT;
g.setColor(color);
g.fillRect(actualX, actualY, WIDTH, HEIGHT);
}
}
/**
* Create and control the game Tetris.
*
* NOTE: when putting a Tetris object in a top-level container, make sure the Tetris
* object has the focus (use requestFocus())
*
15. * @author CSC 143 Based on a C++ version written by the
* University of Washington CSE department
*/
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Tetris extends JPanel
{
private Game theGame;
/** Set up the parts for the Tetris game, display and user control
*/
public Tetris()
{
theGame = new Game(this);
GameController ec = new GameController(theGame);
addKeyListener(ec);
setBackground(Color.yellow);
}
/**
* Update the display
*/
public void update() {
repaint();
}
/**
* Paint the current state of the game
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
theGame.draw(g);
if (theGame.isGameOver() ) {
16. g.setFont(new Font("Palatino", Font.BOLD, 40));
g.setColor(Color.BLACK);
g.drawString("GAME OVER", 80, 300);
}
}
/* Create a game and play it*/
public static void play()
{
JFrame f = new JFrame("The Tetris Game");
Tetris t = new Tetris();
f.getContentPane().add(t);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(400,600);
f.validate();
f.setVisible(true);
t.requestFocus(); // so that Tetris (the JPanel) fires the keyboard events.
}
/**
* To be able to run from the command line or desktop
*/
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
play();
}
});
}
}
And also the class diagram if possible
Thanks
Solution
17. import java.awt.*;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;
import java.awt.event.*;
import java.io.*;
public final class Piece {
// The body of the piece, each point is a coordinate specifying a block
private static Point[] body;
// Each element specifies how high the piece will land in the corresponding column
private int[] skirt;
private int width; // The width of the piece for the current rotation
private int height; // The height of the piece for the current rotation
private static Piece next; // The "next" rotation - note this is how a "piece" is really a list
static private Piece[] pieces; // singleton array of first rotations
private static final int INITIAL_HIGH_VAL = 10;
private static final int INITIAL_ZERO = 0;
private static final int NUMBER_OF_BLOCKS = 4;
//set height and width
//set skirt
private Piece(Point[] points) {
System.out.print("Creating a new piece");
//create the body
//body = new Point[points.length];
//for (int i=0; i< points.length; i++)
18. //body[i] = points[i];
printBody();
//initial value for height and width
height = width = INITIAL_HIGH_VAL;
//sets holders equal to actual points
for(int i = 0; i < body.length; i++){
int x = (int)(body[i].getX());
int y = (int)(body[i].getY());
//set height to greatest found value of y
if(height == INITIAL_HIGH_VAL || height <= y) height = y+1;
//set width to greatest found value of x
if(width == INITIAL_HIGH_VAL || width <= x) width = x+1;
}
System.out.print (" Height = " + height + " Width = " + width);
//creates the skirt of size width
skirt = new int[width];
for (int j=0; j< width; j++) skirt[j]=INITIAL_HIGH_VAL;
for(int j = 0; j < width; j++){
for(int i = 0; i < body.length; i++){
//if the x value is equal to the "x-value"
//of the skirt, then put the y-value for that
//x-value into the skirt, if its the lowest
if((int)(body[i].getX()) == j){
int y = (int)(body[i].getY());
if(y <= skirt[j]) skirt[j] = y;
}
}
}
for (int i=0; i< body.length; i++) {
if (!(body[i].getX() < width)) System.out.println("BAD");
19. }
System.out.println();
}
public int getWidth() {
//System.out.println("Piece tester wanted the width.");
return(width);
}
public int getHeight() {
//System.out.println("Piece tester wanted the height.");
return(height);
}
public Point[] getBody() {
System.out.print("GetBody:");
printBody();
return(body);
}
public void printBody() {
System.out.print("Body:");
for(int i= 0; i