Tetris/Java

From Rosetta Code
Revision as of 11:16, 23 March 2016 by rosettacode>Fwend (→‎{{headerJava}}: added Java)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Java

Works with: java version 8

<lang java>import java.awt.*; import java.awt.event.*; import static java.lang.String.format; import java.util.Arrays; import javax.swing.*;

public class Tetris extends JPanel implements Runnable {

   public static final int EMPTY = -1;
   public static final int BORDER = -2;
   final Color[] colors = {Color.green, Color.red, Color.blue, Color.pink,
       Color.orange, Color.cyan, Color.magenta};
   // for centering the shapes in the preview window
   final int[][] previewOffets = {{16, 15}, {-15, 15}, {0, 0}, {0, 0},
   {-15, 5}, {16, 15}, {-15, 15}};
   int[][] grid;
   Shape selectedShape;
   Shape preSelectedShape;
   int blockSize = 30;
   int nRows = 18;
   int nCols = 12;
   int shapeRow;
   int shapeCol;
   int topMargin = 50;
   int leftMargin = 20;
   Thread fallingThread;
   Scoreboard scoreboard;
   Font smallFont;
   Rectangle gridRect, previewRect;
   public Tetris() {
       setPreferredSize(new Dimension(640, 640));
       setBackground(new Color(0xDDEEFF));
       setFont(new Font("Monospaced", Font.BOLD, 48));
       setFocusable(true);
       smallFont = getFont().deriveFont(Font.BOLD, 18);
       gridRect = new Rectangle(46, 47, 308, 517);
       previewRect = new Rectangle(387, 47, 200, 200);
       scoreboard = new Scoreboard();
       grid = new int[nRows][nCols];
       initGrid();
       selectShape();
       addMouseListener(new MouseAdapter() {
           @Override
           public void mousePressed(MouseEvent e) {
               if (scoreboard.isGameOver()) {
                   startNewGame();
                   repaint();
               }
           }
       });
       addKeyListener(new KeyAdapter() {
           boolean fastDown;
           @Override
           public void keyPressed(KeyEvent e) {
               if (scoreboard.isGameOver())
                   return;
               switch (e.getKeyCode()) {
                   case KeyEvent.VK_UP:
                       if (canRotate(selectedShape))
                           rotate(selectedShape);
                       break;
                   case KeyEvent.VK_LEFT:
                       if (canMove(selectedShape, -1, 0))
                           move(selectedShape, -1, 0);
                       break;
                   case KeyEvent.VK_RIGHT:
                       if (canMove(selectedShape, 1, 0))
                           move(selectedShape, 1, 0);
                       break;
                   case KeyEvent.VK_DOWN:
                       if (!fastDown) {
                           fastDown = true;
                           while (canMove(selectedShape, 0, 1)) {
                               move(selectedShape, 0, 1);
                               repaint();
                           }
                           shapeHasLanded();
                       }
               }
               repaint();
           }
           @Override
           public void keyReleased(KeyEvent e) {
               fastDown = false;
           }
       });
   }
   void selectShape() {
       shapeRow = 1;
       shapeCol = 5;
       selectedShape = preSelectedShape;
       preSelectedShape = Shape.values()[(int) (Math.random() * colors.length)];
       if (selectedShape != null)
           selectedShape.reset();
   }
   void startNewGame() {
       stop();
       initGrid();
       selectShape();
       scoreboard.reset();
       (fallingThread = new Thread(this)).start();
   }
   void stop() {
       if (fallingThread != null) {
           Thread tmp = fallingThread;
           fallingThread = null;
           tmp.interrupt();
       }
   }
   void initGrid() {
       for (int r = 0; r < nRows; r++) {
           Arrays.fill(grid[r], EMPTY);
           for (int c = 0; c < nCols; c++) {
               if (c == 0 || c == nCols - 1 || r == nRows - 1)
                   grid[r][c] = BORDER;
           }
       }
   }
   @Override
   public void run() {
       while (Thread.currentThread() == fallingThread) {
           try {
               Thread.sleep(scoreboard.getSpeed());
           } catch (InterruptedException ignored) {
           }
           if (!scoreboard.isGameOver()) {
               if (canMove(selectedShape, 0, 1)) {
                   move(selectedShape, 0, 1);
               } else {
                   shapeHasLanded();
               }
               repaint();
           }
       }
   }
   void drawStartScreen(Graphics2D g) {
       g.setFont(getFont());
       g.setColor(new Color(0xFFFFFF));
       g.fillRect(leftMargin + 80, topMargin + 35, 252, 100);
       g.fillRect(leftMargin + 30, topMargin + 325, 252, 40);
       g.setColor(Color.black);
       g.drawString("Tetris", leftMargin + 110, topMargin + 100);
       g.setFont(smallFont);
       g.drawString("click to start", leftMargin + 100, topMargin + 350);
   }
   void drawSquare(Graphics2D g, Color color, int r, int c) {
       g.setStroke(new BasicStroke(2));
       g.setColor(color);
       g.fillRect(leftMargin + c * blockSize, topMargin + r * blockSize,
               blockSize, blockSize);
       g.setColor(Color.white);
       g.drawRect(leftMargin + c * blockSize, topMargin + r * blockSize,
               blockSize, blockSize);
   }
   void drawUI(Graphics2D g) {
       g.setStroke(new BasicStroke(5));
       g.setColor(new Color(0xBECFEA));
       g.fill(gridRect);
       g.setColor(new Color(0x7788AA));
       g.draw(gridRect);
       g.draw(previewRect);
       for (int r = 0; r < nRows; r++) {
           for (int c = 0; c < nCols; c++) {
               int id = grid[r][c];
               if (id > EMPTY)
                   drawSquare(g, colors[id], r, c);
           }
       }
       g.setColor(Color.black);
       g.setFont(smallFont);
       g.drawString(format("hiscore  %6d", scoreboard.getTopscore()),400, 330);
       g.drawString(format("level    %6d", scoreboard.getLevel()), 400, 360);
       g.drawString(format("lines    %6d", scoreboard.getLines()), 400, 390);
       g.drawString(format("score    %6d", scoreboard.getScore()), 400, 420);
   }
   void drawPreview(Graphics2D g) {
       int ord = preSelectedShape.ordinal();
       g.translate(previewOffets[ord][0], previewOffets[ord][1]);
       for (int[] p : preSelectedShape.shapes[ord]) {
           drawSquare(g, colors[ord], 2 + p[1], 15 + p[0]);
       }
       g.translate(-previewOffets[ord][0], -previewOffets[ord][1]);
   }
   void drawFallingShape(Graphics2D g) {
       Color c = colors[selectedShape.ordinal()];
       for (int[] p : selectedShape.pos)
           drawSquare(g, c, shapeRow + p[1], shapeCol + p[0]);
   }
   @Override
   public void paintComponent(Graphics gg) {
       super.paintComponent(gg);
       Graphics2D g = (Graphics2D) gg;
       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
               RenderingHints.VALUE_ANTIALIAS_ON);
       drawUI(g);
       if (fallingThread == null) {
           drawStartScreen(g);
       } else {
           drawFallingShape(g);
       }
       drawPreview(g);
   }
   boolean canRotate(Shape s) {
       if (s == Shape.Square)
           return false;
       int[][] pos = new int[4][2];
       for (int i = 0; i < pos.length; i++) {
           pos[i] = s.pos[i].clone();
       }
       for (int[] row : pos) {
           int tmp = row[0];
           row[0] = row[1];
           row[1] = -tmp;
       }
       for (int[] p : pos) {
           int newCol = shapeCol + p[0];
           int newRow = shapeRow + p[1];
           if (grid[newRow][newCol] != EMPTY) {
               return false;
           }
       }
       return true;
   }
   void rotate(Shape s) {
       if (s == Shape.Square)
           return;
       for (int[] row : s.pos) {
           int tmp = row[0];
           row[0] = row[1];
           row[1] = -tmp;
       }
   }
   void move(Shape s, int xIncr, int yIncr) {
       shapeRow += yIncr;
       shapeCol += xIncr;
   }
   boolean canMove(Shape s, int xIncr, int yIncr) {
       for (int[] p : s.pos) {
           int newCol = shapeCol + xIncr + p[0];
           int newRow = shapeRow + yIncr + p[1];
           if (grid[newRow][newCol] != EMPTY)
               return false;
       }
       return true;
   }
   void shapeHasLanded() {
       addShape(selectedShape);
       if (shapeRow < 2) {
           scoreboard.setGameOver();
           scoreboard.setTopscore();
           stop();
       } else {
           scoreboard.addLines(removeLines());
       }
       selectShape();
   }
   int removeLines() {
       int count = 0;
       for (int r = 0; r < nRows - 1; r++) {
           for (int c = 1; c < nCols - 1; c++) {
               if (grid[r][c] == EMPTY)
                   break;
               if (c == nCols - 2) {
                   count++;
                   removeLine(r);
               }
           }
       }
       return count;
   }
   void removeLine(int line) {
       for (int c = 0; c < nCols; c++)
           grid[line][c] = EMPTY;
       for (int c = 0; c < nCols; c++) {
           for (int r = line; r > 0; r--)
               grid[r][c] = grid[r - 1][c];
       }
   }
   void addShape(Shape s) {
       for (int[] p : s.pos)
           grid[shapeRow + p[1]][shapeCol + p[0]] = s.ordinal();
   }
   public static void main(String[] args) {
       SwingUtilities.invokeLater(() -> {
           JFrame f = new JFrame();
           f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
           f.setTitle("Tetris");
           f.setResizable(false);
           f.add(new Tetris(), BorderLayout.CENTER);
           f.pack();
           f.setLocationRelativeTo(null);
           f.setVisible(true);
       });
   }

}

class Scoreboard {

   static final int MAXLEVEL = 9;
   private int level;
   private int lines;
   private int score;
   private int topscore;
   private boolean gameOver = true;
   void reset() {
       setTopscore();
       level = lines = score = 0;
       gameOver = false;
   }
   void setGameOver() {
       gameOver = true;
   }
   boolean isGameOver() {
       return gameOver;
   }
   void setTopscore() {
       if (score > topscore)
           topscore = score;
   }
   int getTopscore() {
       return topscore;
   }
   int getSpeed() {
       switch (level) {
           case 0:
               return 700;
           case 1:
               return 600;
           case 2:
               return 500;
           case 3:
               return 400;
           case 4:
               return 350;
           case 5:
               return 300;
           case 6:
               return 250;
           case 7:
               return 200;
           case 8:
               return 150;
           case 9:
               return 100;
           default:
               return 100;
       }
   }
   void addScore(int sc) {
       score += sc;
   }
   void addLines(int line) {
       switch (line) {
           case 1:
               addScore(10);
               break;
           case 2:
               addScore(20);
               break;
           case 3:
               addScore(30);
               break;
           case 4:
               addScore(40);
               break;
           default:
               return;
       }
       lines += line;
       if (lines > 10)
           addLevel();
   }
   void addLevel() {
       lines %= 10;
       if (level < MAXLEVEL)
           level++;
   }
   int getLevel() {
       return level;
   }
   int getLines() {
       return lines;
   }
   int getScore() {
       return score;
   }

}

enum Shape {

   ZShape, SShape, Straight, TShape, Square, LShape, JShape;
   private Shape() {
       pos = new int[4][2];
       reset();
   }
   void reset() {
       for (int i = 0; i < pos.length; i++) {
           pos[i] = shapes[ordinal()][i].clone();
       }
   }
   final int[][] pos;
   final int[][][] shapes = {
       {{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}}};

}</lang>