import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.HeadlessException;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
public class Main extends JFrame {
public static final int DOWN = 0;
public static final int LEFT = 1;
public static final int RIGHT = 2;
public static final int SPACE = 9;
public static final int WIDTH = 800;
public static final int HEIGHT = 600;
public static final int FIELD_WIDTH = 10 * Polygon.UNIT;
public static final Color BACKGROUND_COLOR = Color.BLACK;
public int currentKey = -1;
private final int GRID_HEIGHT = HEIGHT / Polygon.UNIT;
private final int[][] GRID = new int[10][GRID_HEIGHT];
private final Font SCORE_FONT = new Font("TimesRoman", Font.PLAIN, 50);
private final List POLYGON_LIST;
private int difficulty = 500;//ms, smaller number is higher difficulty
private long score;
private boolean gameOver = false;
public Main() throws HeadlessException {
super("TeTrIs");
this.setSize(WIDTH, HEIGHT);
this.setBackground(Color.BLACK);
this.addKeyListener(new Input());
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
POLYGON_LIST = new ArrayList();
}
public void paint(Graphics graphics) {
Graphics2D graphics2D = (Graphics2D) graphics;
graphics2D.clearRect(0, 0, WIDTH, HEIGHT);
for (int a = 0; a < POLYGON_LIST.size(); a++) {
((Polygon) POLYGON_LIST.get(a)).draw(graphics2D);
}
//border
graphics2D.setPaint(Color.WHITE);
graphics2D.drawRect(0, 0, Polygon.UNIT, HEIGHT);
graphics2D.drawRect(FIELD_WIDTH + Polygon.UNIT, 0, Polygon.UNIT, HEIGHT);
//score
graphics2D.setFont(SCORE_FONT);
graphics2D.drawString("Score: " + score, FIELD_WIDTH + 50, 100);
if (gameOver) {
graphics2D.drawString("GAME OVER", FIELD_WIDTH + 150, 300);
}
}
public static void main(String[] args) {
// TODO code application logic here
Main game = new Main();
Random random = new Random();
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'Z'));
long time = new Date().getTime();
while (true) {
if (game.isGameOver()) {
game.getCurrentPolygon().setInactive();
game.gameOver = true;
break;
}
//is it time to push polygon down
if (new Date().getTime() > time + game.difficulty) {
time = new Date().getTime();
if (game.isMovementAllowed(DOWN)) {
game.getCurrentPolygon().move(DOWN);
} else {
game.getCurrentPolygon().setInactive();
}
game.repaint();
}
//if current polygon is dead
if (!game.getCurrentPolygon().isActive()) {
game.addPolygonToGrid();
game.removeCompleteLines();
//get next polygon
switch (random.nextInt(7)) {
case 0:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'I'));
break;
case 1:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'L'));
break;
case 2:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'J'));
break;
case 3:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'Z'));
break;
case 4:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'T'));
break;
case 5:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'S'));
break;
case 6:
game.POLYGON_LIST.add(new Polygon(FIELD_WIDTH / 2, 50, new Color(random.nextInt()), 'O'));
break;
}
}
}
}
public Polygon getCurrentPolygon() {
return (Polygon) POLYGON_LIST.get(POLYGON_LIST.size() - 1);
}
private boolean isMovementAllowed(int direction) {
for (int a = 0; a < getCurrentPolygon().getCubes().length; a++) {
Cube temp = getCurrentPolygon().getCubes()[a];
int x = temp.getUnitX();
int y = temp.getUnitY();
switch (direction) {
case DOWN:
if (y >= GRID_HEIGHT || GRID[x - 1][y] == 1) {
return false;
}
break;
case LEFT:
if (x <= 1 || GRID[x - 2][y - 1] == 1) {
return false;
}
break;
case RIGHT:
if (x >= 10 || GRID[x][y - 1] == 1) {
return false;
}
break;
}
}
return true;
}
private boolean isRotationInbounds() {
for (int a = 0; a < getCurrentPolygon().getNewCubes().length; a++) {
int x = getCurrentPolygon().getNewCubes()[a].getX();
int y = getCurrentPolygon().getNewCubes()[a].getY();
if (x < Polygon.UNIT
|| x > FIELD_WIDTH
|| y < Polygon.UNIT
|| y > HEIGHT - Polygon.UNIT
|| GRID[(x / Polygon.UNIT) - 1][(y / Polygon.UNIT) - 1] == 1) {
return false;
}
}
return true;
}
private void addPolygonToGrid() {
for (int a = 0; a < getCurrentPolygon().getCubes().length; a++) {
Cube temp = getCurrentPolygon().getCubes()[a];
int x = temp.getUnitX();
int y = temp.getUnitY();
GRID[x - 1][y - 1] = 1;
}
}
private void removeCompleteLines() {
int completedLines = 0;
for (int y = 0; y < GRID[0].length; y++) {
boolean removeLine = true;
for (int x = 0; x < GRID.length; x++) {
if (GRID[x][y] == 0) {//disabled node
removeLine = false;
break;
}
//remove line
}
if (removeLine) {
removeLineAndShiftGrid(y);
removeLineAndShiftPolygons(y);
++completedLines;
}
}
incrementScore(completedLines);
}
private void removeLineAndShiftGrid(int lineNumber) {
for (int y = lineNumber; y >= 0; --y) {
for (int x = 0; x < GRID.length; x++) {
if (y > 0) {
GRID[x][y] = GRID[x][y - 1];
} else {
GRID[x][0] = 0;
}
}
}
}
private void removeLineAndShiftPolygons(int lineNumber) {
lineNumber *= Polygon.UNIT;
lineNumber += Polygon.UNIT;
for (int a = 0; a < POLYGON_LIST.size(); a++) {
Polygon polygon = (Polygon) POLYGON_LIST.get(a);
for (int b = 0; b < polygon.getCubes().length; b++) {
Cube cube = polygon.getCubes()[b];
if (cube.getY() == lineNumber) {
cube.setColor(Color.BLACK);
} else if (cube.getY() < lineNumber) {
cube.setY(cube.getY() + Polygon.UNIT);
}
}
}
clearDeadPolygons();
}
private void clearDeadPolygons() {
for (int a = 0; a < POLYGON_LIST.size(); a++) {
boolean dead = true;
Cube[] cubes = ((Polygon) POLYGON_LIST.get(a)).getCubes();
for (int b = 0; b < cubes.length; b++) {
Cube cube = cubes[b];
if (cube.getColor() != BACKGROUND_COLOR) {
dead = false;
break;
}
}
if (dead) {
POLYGON_LIST.remove(a--);
}
}
}
private void incrementScore(int completedLines) {
switch (completedLines) {
case 1:
score += 10;
break;
case 2:
score += 20;
break;
case 3:
score += 60;
break;
case 4:
score += 240;
break;
default:
break;
}
}
private boolean isGameOver() {
if (GRID[5][0] == 1) {
return true;
}
return false;
}
class Input implements KeyListener {
private boolean downKey, leftKey, rightKey, rotateKey;
public void keyTyped(KeyEvent e) {
//do nothing
}
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_DOWN:
if (!downKey && isMovementAllowed(DOWN)) {
currentKey = DOWN;
downKey = true;
}
break;
case KeyEvent.VK_LEFT:
if (!leftKey && isMovementAllowed(LEFT)) {
currentKey = LEFT;
leftKey = true;
}
break;
case KeyEvent.VK_RIGHT:
if (!rightKey && isMovementAllowed(RIGHT)) {
currentKey = RIGHT;
rightKey = true;
}
break;
case KeyEvent.VK_SPACE:
if (!rotateKey) {
getCurrentPolygon().calculateRotation();
if (isRotationInbounds()) {
getCurrentPolygon().applyRotation();
repaint();
} else {
getCurrentPolygon().restore();
}
rotateKey = true;
}
break;
/*
case KeyEvent.VK_ENTER:
getCurrentPolygon().setInactive();
repaint();
break;
*/
case KeyEvent.VK_F12:
for (int y = 0; y < GRID[0].length; y++) {
for (int x = 0; x < GRID.length; x++) {
System.out.print(GRID[x][y]);
}
System.out.println();
}
break;
case KeyEvent.VK_ESCAPE:
System.out.println("< good bye >");
System.exit(0);
break;
}
if (currentKey > -1) {
getCurrentPolygon().move(currentKey);
repaint();
}
currentKey = -1;
}
public void keyReleased(KeyEvent e) {
//reset keys
downKey = leftKey = rightKey = rotateKey = false;
}
}
}