import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
/**
* A two dimensions interger array wrapper class. It privides such semantics that if an element's value equals to 0,
* we say this element is unoccupied, if an element's value do not equal to 0, we say this element is occupied.
*/
class IntMatrix implements Cloneable {
/**
* Contruct an IntMatrix with initial value.
*/
public IntMatrix(int height, int width, int initialValue) {
_height = height;
_width = width;
_data = new int[_height][_width];
for (int i = 0; i < _height; i++)
for (int j = 0; j < _width; j++)
_data[i][j] = initialValue;
}
/**
* Copy contructor.
*/
public IntMatrix(IntMatrix rhs) {
_height = rhs.getHeight();
_width = rhs.getWidth();
_data = new int[_height][_width];
for (int i = 0; i < _height; i++)
for (int j = 0; j < _width; j++)
_data[i][j] = rhs.get(i, j);
}
/**
* Contruct an IntMatrix with all elements unoccupied.
*/
public IntMatrix(int height, int width) {
this(height, width, 0);
}
/**
* Return height.
*/
public int getHeight() { return _height; }
/**
* Return width.
*/
public int getWidth() { return _width; }
/**
* Set the value of an element.
*/
public void set(int row, int column, int value) {
_data[row][column] = value;
}
/**
* Return the value of an element.
*/
public int get(int row, int column) {
return _data[row][column];
}
/**
* Return true if the other IntMatrix can be placed in this one.
*/
public boolean contains(IntMatrix other, Position pos) {
return partlyContains(other, pos, 0);
}
/**
* Return true if part of the other IntMatrix can be placed in this one.
*/
public boolean partlyContains(IntMatrix other, Position pos, int begin) {
if (pos.getRow() < 0 || pos.getColumn() < 0)
return false;
if (pos.getColumn() + other.getWidth() > this._width)
return false;
if (pos.getRow() + other.getHeight() - begin > this._height)
return false;
for (int i = begin; i < other.getHeight(); i++) {
for (int j = 0; j < other.getWidth(); j++) {
if (other.get(i, j) > 0 &&
this.get(i + pos.getRow() - begin,
j + pos.getColumn()) > 0)
return false;
}
}
return true;
}
/**
* Add the other IntMatrix to this one.
*/
public void add(IntMatrix other, Position pos) {
for (int i = 0; i < other.getHeight(); i++) {
for (int j = 0; j < other.getWidth(); j++) {
if (other.get(i, j) > 0)
this.set(pos.getRow() + i,
pos.getColumn() + j,
other.get(i, j));
}
}
}
/**
* Return true if all the elements in the specified row are occupied.
*/
public boolean isRowOccupied(int index) {
for (int i = 0; i < _width; i++) {
if (_data[index][i] == 0) return false;
}
return true;
}
/**
* Delete the specified row and move down the rows above.
*/
public void deleteRow(int index) {
for (int i = index; i > 0; i--)
for (int j = 0; j < _width; j++) {
_data[i][j] = _data[i-1][j];
}
clearRow(0);
}
/**
* Set all elements to unoccupied.
*/
public void clear() {
for (int i = 0; i < _height; i++) {
clearRow(i);
}
}
/**
* Rotate the source IntMatrix clockwise and return the new created one.
*/
public static IntMatrix transform(IntMatrix source) {
IntMatrix target = new IntMatrix(source.getWidth(), source.getHeight());
for (int i = 0; i < target.getHeight(); i++)
for (int j = 0; j < target.getWidth(); j++)
target.set(i, j, source.get(source.getHeight() - j - 1, i));
return target;
}
/**
* Clone a instance.
*/
public Object clone() {
IntMatrix o = null;
try {
o = (IntMatrix)super.clone();
} catch (CloneNotSupportedException e) {}
o._height = this._height;
o._width = this._width;
o._data = (int[][])this._data.clone();
return o;
}
/**
* Dump this IntMatrix for debug.
*/
public void dump() {
System.out.println("<<------------->>");
System.out.print("Height=");System.out.print(_height);
System.out.print(" Width=");System.out.print(_width);
System.out.println();
for (int i = 0; i < _height; i++) {
for (int j = 0; j < _width; j++) {
System.out.print(_data[i][j]);
System.out.print(" ");
}
System.out.println();
}
}
private void clearRow(int index) {
for (int j = 0; j < _width; j++) {
_data[index][j] = 0;
}
}
private int _height;
private int _width;
private int _data[][];
}
/**
* A helper class to hold the position of an IntMatrix.
*/
class Position {
/**
* Contruct a Postion.
*/
public Position(int row, int column) {
_row = row;
_column = column;
}
/**
* Copy constructor.
*/
public Position(Position rhs) {
this(rhs.getRow(), rhs.getColumn());
}
/**
* Contruct a Position with row and column to be zero.
*/
public Position() { this(0, 0); }
/**
* Return current row.
*/
public int getRow() { return _row; }
/**
* Set row.
*/
public void setRow(int row) { _row = row; }
/**
* Return current column.
*/
public int getColumn() { return _column; }
/**
* Set column.
*/
public void setColumn(int column) { _column = column; }
/**
* Set position equals to another one.
*/
public void setPosition(Position pos) {
_row = pos.getRow();
_column = pos.getColumn();
}
private int _row;
private int _column;
}
/**
* View interface of the tetris game. It is the VIEW in M-VC pattern.
*/
interface TetrisView {
/**
* Set the tetris model of the view to establish the two-way association.
* This method will be invoked by TetirsModel.setView().
*/
void setModel(TetrisModel model);
/**
* Notify the view that the main map is changed.
*/
void mapChanged();
/**
* Notify the view that the score is changed.
*/
void scoreChanged();
/**
* Notify the view that the preview cube is changed.
*/
void previewChanged();
/**
* Notify the view that there are rows will be deleted in the map.
*/
void rowsToDelete(int row[], int count);
/**
* Notify the view that the game is over.
*/
void gameOver();
}
/**
* The model of tetris game. It's the MODEL of M-VC pattern.
*/
class TetrisModel implements Runnable {
/**
* Contructor
*/
public TetrisModel(int height, int width) {
_map = new IntMatrix(height, width);
_viewMap = new IntMatrix(height, width);
_cube = new ActiveCube(_map);
}
/**
* Set the view of this model.
*/
public void setView(TetrisView view) {
_view = view;
_view.setModel(this);
}
/**
* Start the game.
*/
public void start() {
_stopped = false;
_map.clear();
_cube.next(getNextCube());
update();
_score = 0;
_view.scoreChanged();
Thread t = new Thread(this);
t.start();
}
/**
* Stop the game.
*/
public synchronized void stop() {
_stopped = true;
resume();
_map.clear();
update();
}
/**
* Return true if the game is stopped.
*/
public synchronized boolean isStopped() { return _stopped; }
/**
* Pause the game.
*/
public synchronized void pause() {
_paused = true;
}
/**
* Continue the game when paused.
*/
public synchronized void resume() {
_paused = false;
notify();
}
/**
* Return true if the game is paused.
*/
public synchronized boolean isPaused() { return _paused; }
/**
* Move the cube to left.
*/
public void left() {
if (isStoppedOrPaused()) return;
if (_cube.left()) update();
}
/**
* Move the cube to right.
*/
public void right() {
if (isStoppedOrPaused()) return;
if (_cube.right()) update();
}
/**
* Rotate the cube.
*/
public void rotate() {
if (isStoppedOrPaused()) return;
if (_cube.rotate()) u