Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Write a Java application called NewTetris that allows a new way to play Tetris.

ID: 3533696 • Letter: W

Question

Write a Java application called NewTetris that allows a new way to play Tetris. It is well known that in Tetris there are seven kinds of tetriminos, each of which is made up of four minos.The application window is divided into three bands: top, middle and bottom. When the application starts, seven different tetriminos will appear within the top band, as shown in the Java applet below. These seven tetrinimos are referred to as model tetriminos. A user can add tetriminos into the middle band tocreate a meaningful shape. A tetrimino that is no longer useful can be deleted byputting it into the bottom band.

Specifically, a user can perform the following four operations to create a meaningful shape in the middle band.

Add: to add a tetrimino, a user can use the left mouse button to drag the corresponding model tetrimino located within the top band. When a model tetrimino is selected to be dragged, a duplicate is made out of the model. It is the duplicate that is actually dragged. The model tetrimino stays at the same location.

Move: To move a duplicate tetrimino, use the left mouse button to drag it. The model tetrimino can't be moved.

Rotate: To rotate a duplicate tetrimino, use the right mouse button to click one of its four minos. Please note the tetrimino will rotate about the center of the mino that is clicked. The model tetrimino can't be rotated.

Delete: at the end of the moving or rotating operation, if the tetrimino intersects with the bottom band, the tetrimino will be deleted. Since a model tetrimino can't be moved or rotated, it can't be deleted.


Requirements/Recommendations:

You shall write a class named CMino to describe a mino.

You shall write a class named CTetriMino to describe seven different tetriminos.

You can use collections to keep a record of all the tetriminos added so far.

Use event handlers to add, move, rotate and delete a tetrimino.

After rotating a point (x1, y1) about another point (x0, y0) counterclockwise through an angle of theta in radians, its new location (x2, y2) can be represented as follows.

x2=x0+(x1-x0)*cos(theta)-(y1-y0)*sin(theta)

y2=y0+(x1-x0)*sin(theta)+(y1-y0)*cos(theta)


Next:

Extend the NewTetris application to be a two-user network application that allows two users on two different machines to play the NewTetris game together. Both users have their own application windows on their own machines. Whatever one user has performed an operation within his/her window will be immediately reflected on the window of the other user too. Therefore both windows will show exactly the same tetrimino collection.

Difficulty: this is a typical distributed computing project. The difficulty is how to handle the conflicting operations simultaneously performed by the two users. For example, what to do when two users want to drag the same tetrimino at the same time? At the minimum, you can assume that the users will make sure no conflicting operations will be performed by themselves. If you choose to implement an advanced solution, please describe your idea in the email message body.

Explanation / Answer

Tetris.java:

package tetris;


import java.awt.BorderLayout;


import javax.swing.JFrame;

import javax.swing.JLabel;



public class Tetris extends JFrame {


JLabel statusbar;



public Tetris() {


statusbar = new JLabel(" 0");

add(statusbar, BorderLayout.SOUTH);

Board board = new Board(this);

add(board);

board.start();


setSize(200, 400);

setTitle("Tetris");

setDefaultCloseOperation(EXIT_ON_CLOSE);

}


public JLabel getStatusBar() {

return statusbar;

}


public static void main(String[] args) {


Tetris game = new Tetris();

game.setLocationRelativeTo(null);

game.setVisible(true);


}

}

Shape.java

package tetris;


import java.util.Random;

import java.lang.Math;



public class Shape {


enum Tetrominoes { NoShape, ZShape, SShape, LineShape,

TShape, SquareShape, LShape, MirroredLShape };


private Tetrominoes pieceShape;

private int coords[][];

private int[][][] coordsTable;



public Shape() {


coords = new int[4][2];

setShape(Tetrominoes.NoShape);


}


public void setShape(Tetrominoes shape) {


coordsTable = new int[][][] {

{ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },

{ { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } },

{ { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } },

{ { 0, -1 }, { 0, 0 }, { 0, 1 }, { 0, 2 } },

{ { -1, 0 }, { 0, 0 }, { 1, 0 }, { 0, 1 } },

{ { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } },

{ { -1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } },

{ { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } }

};


for (int i = 0; i < 4 ; i++) {

for (int j = 0; j < 2; ++j) {

coords[i][j] = coordsTable[shape.ordinal()][i][j];

}

}

pieceShape = shape;


}


private void setX(int index, int x) { coords[index][0] = x; }

private void setY(int index, int y) { coords[index][1] = y; }

public int x(int index) { return coords[index][0]; }

public int y(int index) { return coords[index][1]; }

public Tetrominoes getShape() { return pieceShape; }


public void setRandomShape()

{

Random r = new Random();

int x = Math.abs(r.nextInt()) % 7 + 1;

Tetrominoes[] values = Tetrominoes.values();

setShape(values[x]);

}


public int minX()

{

int m = coords[0][0];

for (int i=0; i < 4; i++) {

m = Math.min(m, coords[i][0]);

}

return m;

}



public int minY()

{

int m = coords[0][1];

for (int i=0; i < 4; i++) {

m = Math.min(m, coords[i][1]);

}

return m;

}


public Shape rotateLeft()

{

if (pieceShape == Tetrominoes.SquareShape)

return this;


Shape result = new Shape();

result.pieceShape = pieceShape;


for (int i = 0; i < 4; ++i) {

result.setX(i, y(i));

result.setY(i, -x(i));

}

return result;

}


public Shape rotateRight()

{

if (pieceShape == Tetrominoes.SquareShape)

return this;


Shape result = new Shape();

result.pieceShape = pieceShape;


for (int i = 0; i < 4; ++i) {

result.setX(i, -y(i));

result.setY(i, x(i));

}

return result;

}

}

Board.java:


package tetris;


import java.awt.Color;

import java.awt.Dimension;

import java.awt.Graphics;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.awt.event.KeyAdapter;

import java.awt.event.KeyEvent;


import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.Timer;


import tetris.Shape.Tetrominoes;



public class Board extends JPanel implements ActionListener {



final int BoardWidth = 10;

final int BoardHeight = 22;


Timer timer;

boolean isFallingFinished = false;

boolean isStarted = false;

boolean isPaused = false;

int numLinesRemoved = 0;

int curX = 0;

int curY = 0;

JLabel statusbar;

Shape curPiece;

Tetrominoes[] board;




public Board(Tetris parent) {


setFocusable(true);

curPiece = new Shape();

timer = new Timer(400, this);

timer.start();


statusbar = parent.getStatusBar();

board = new Tetrominoes[BoardWidth * BoardHeight];

addKeyListener(new TAdapter());

clearBoard();

}


public void actionPerformed(ActionEvent e) {

if (isFallingFinished) {

isFallingFinished = false;

newPiece();

} else {

oneLineDown();

}

}



int squareWidth() { return (int) getSize().getWidth() / BoardWidth; }

int squareHeight() { return (int) getSize().getHeight() / BoardHeight; }

Tetrominoes shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; }



public void start()

{

if (isPaused)

return;


isStarted = true;

isFallingFinished = false;

numLinesRemoved = 0;

clearBoard();


newPiece();

timer.start();

}


private void pause()

{

if (!isStarted)

return;


isPaused = !isPaused;

if (isPaused) {

timer.stop();

statusbar.setText("paused");

} else {

timer.start();

statusbar.setText(String.valueOf(numLinesRemoved));

}

repaint();

}


public void paint(Graphics g)

{

super.paint(g);


Dimension size = getSize();

int boardTop = (int) size.getHeight() - BoardHeight * squareHeight();



for (int i = 0; i < BoardHeight; ++i) {

for (int j = 0; j < BoardWidth; ++j) {

Tetrominoes shape = shapeAt(j, BoardHeight - i - 1);

if (shape != Tetrominoes.NoShape)

drawSquare(g, 0 + j * squareWidth(),

boardTop + i * squareHeight(), shape);

}

}


if (curPiece.getShape() != Tetrominoes.NoShape) {

for (int i = 0; i < 4; ++i) {

int x = curX + curPiece.x(i);

int y = curY - curPiece.y(i);

drawSquare(g, 0 + x * squareWidth(),

boardTop + (BoardHeight - y - 1) * squareHeight(),

curPiece.getShape());

}

}

}


private void dropDown()

{

int newY = curY;

while (newY > 0) {

if (!tryMove(curPiece, curX, newY - 1))

break;

--newY;

}

pieceDropped();

}


private void oneLineDown()

{

if (!tryMove(curPiece, curX, curY - 1))

pieceDropped();

}



private void clearBoard()

{

for (int i = 0; i < BoardHeight * BoardWidth; ++i)

board[i] = Tetrominoes.NoShape;

}


private void pieceDropped()

{

for (int i = 0; i < 4; ++i) {

int x = curX + curPiece.x(i);

int y = curY - curPiece.y(i);

board[(y * BoardWidth) + x] = curPiece.getShape();

}


removeFullLines();


if (!isFallingFinished)

newPiece();

}


private void newPiece()

{

curPiece.setRandomShape();

curX = BoardWidth / 2 + 1;

curY = BoardHeight - 1 + curPiece.minY();


if (!tryMove(curPiece, curX, curY)) {

curPiece.setShape(Tetrominoes.NoShape);

timer.stop();

isStarted = false;

statusbar.setText("game over");

}

}


private boolean tryMove(Shape newPiece, int newX, int newY)

{

for (int i = 0; i < 4; ++i) {

int x = newX + newPiece.x(i);

int y = newY - newPiece.y(i);

if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight)

return false;

if (shapeAt(x, y) != Tetrominoes.NoShape)

return false;

}


curPiece = newPiece;

curX = newX;

curY = newY;

repaint();

return true;

}


private void removeFullLines()

{

int numFullLines = 0;


for (int i = BoardHeight - 1; i >= 0; --i) {

boolean lineIsFull = true;


for (int j = 0; j < BoardWidth; ++j) {

if (shapeAt(j, i) == Tetrominoes.NoShape) {

lineIsFull = false;

break;

}

}


if (lineIsFull) {

++numFullLines;

for (int k = i; k < BoardHeight - 1; ++k) {

for (int j = 0; j < BoardWidth; ++j)

board[(k * BoardWidth) + j] = shapeAt(j, k + 1);

}

}

}


if (numFullLines > 0) {

numLinesRemoved += numFullLines;

statusbar.setText(String.valueOf(numLinesRemoved));

isFallingFinished = true;

curPiece.setShape(Tetrominoes.NoShape);

repaint();

}

}


private void drawSquare(Graphics g, int x, int y, Tetrominoes shape)

{

Color colors[] = { new Color(0, 0, 0), new Color(204, 102, 102),

new Color(102, 204, 102), new Color(102, 102, 204),

new Color(204, 204, 102), new Color(204, 102, 204),

new Color(102, 204, 204), new Color(218, 170, 0)

};



Color color = colors[shape.ordinal()];


g.setColor(color);

g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2);


g.setColor(color.brighter());

g.drawLine(x, y + squareHeight() - 1, x, y);

g.drawLine(x, y, x + squareWidth() - 1, y);


g.setColor(color.darker());

g.drawLine(x + 1, y + squareHeight() - 1,

x + squareWidth() - 1, y + squareHeight() - 1);

g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1,

x + squareWidth() - 1, y + 1);


}


class TAdapter extends KeyAdapter {

public void keyPressed(KeyEvent e) {


if (!isStarted || curPiece.getShape() == Tetrominoes.NoShape) {

return;

}


int keycode = e.getKeyCode();


if (keycode == 'p' || keycode == 'P') {

pause();

return;

}


if (isPaused)

return;


switch (keycode) {

case KeyEvent.VK_LEFT:

tryMove(curPiece, curX - 1, curY);

break;

case KeyEvent.VK_RIGHT:

tryMove(curPiece, curX + 1, curY);

break;

case KeyEvent.VK_DOWN:

tryMove(curPiece.rotateRight(), curX, curY);

break;

case KeyEvent.VK_UP:

tryMove(curPiece.rotateLeft(), curX, curY);

break;

case KeyEvent.VK_SPACE:

dropDown();

break;

case 'd':

oneLineDown();

break;

case 'D':

oneLineDown();

break;

}


}

}

}


Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote