I am starting a new BlueJ project.

I define two classes: Board and Tetris.

Tetris is a JFrame. Board is a JPanel.

A Game is a (virtual) World with some Actors in it that act.

Tetris is a Game. Board is a World. We have yet to define the actors.

Notice we start the world, but it’s an empty world so nothing happens.

Now I define a Shape, which is a kind of Actor. It rains with shapes.

When they reach the ground they turn into something else, as they deposit on the ground.

By some chemical reaction full lines vanish. We can influence the rain of individual shapes.

Let’s see how we do that.

Here’s the basic class diagram (interfaces are really empty):


 

Here’s what the code looks like for Board and Shape at this stage:

Here’s the code for the Game class:

Here’s how the game looks if you start it (by invoking its main method):

          

http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

If you recall the Robot problem we used numbers and Strings to encode direction. We could have used enum types as indicated in the tutorial above. We will use enum types in this program for game pieces. To understand this new type we made the test shown above.  Here’s a slightly longer test:

So now we know how to use enum types, I guess. Each tetromino is made out four little squares. Let’s see if you can guess which teromino is described by this line:

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

 

 

Let’s draw it to find out:

             

The set of four points corresponds to the TShape. Here’s the game being playes such that no line is formed. TShape is yellow, there are two in picture on the right which also shows the rest of shapes.

Now what do we do?

We need a timer in our world.

This means Board changes.

I am now ready to start all over.

Let’s ask ourselves: who is supposed to listen to the keys? The World or the Game? I say the Game.

And I need a timer in the world as we discussed.

So here now is the current/basic decomposition (see next page).

Where do we go from here?

I want to understand the drawing.

So I move into DrJava. It’s going to be easier to control multiple files.

      

 

So here’s what I have right now:


 

import java.awt.*;

import javax.swing.*;

import java.awt.event.*;

 

public class Game extends JFrame {

  World world;

  public Game() {

    this.world = new World(this);

    add(world);

    setSize(200, 400);

    setTitle("Tetris");

    setDefaultCloseOperation(EXIT_ON_CLOSE);

    world.start();

  }

  public static void main(String[] args) {

    Game game = new Game();

    game.setVisible(true);

  }

 

}


 

import javax.swing.*;

import java.awt.*;

import java.awt.event.*;

 

public class World extends JPanel implements KeyListener,

                                             ActionListener

{   Timer timer;

    Shape[] shapes = new Shape[8];

    public World(Game parent) {

        parent.addKeyListener(this);

        timer = new Timer(400, this);

        // this.shape = new TShape(this);

        shapes[0] = new LineShape(this);     

        shapes[0].curX =  1; shapes[0].curY =  6;

        shapes[1] = new LShape(this);        

        shapes[1].curX =  2; shapes[1].curY = 14;

        shapes[2] = new MirroredLShape(this);

        shapes[2].curX =  3; shapes[2].curY =  9;

        shapes[3] = new NoShape(this);       

        shapes[3].curX =  4; shapes[3].curY = 20;

        shapes[4] = new SquareShape(this);   

        shapes[4].curX =  5; shapes[4].curY =  7;

        shapes[5] = new SShape(this);        

        shapes[5].curX =  6; shapes[5].curY = 12;

        shapes[6] = new ZShape(this);        

        shapes[6].curX =  7; shapes[6].curY = 17;

        shapes[7] = new TShape(this);        

        shapes[7].curX =  7; shapes[7].curY = 2;

               

    }

    public void keyPressed(KeyEvent e) {

        System.out.println( "... Key pressed: " + e ); }

    public void keyReleased(KeyEvent e) {

        System.out.println( "... Key typed: " + e );   }

    public void keyTyped(KeyEvent e) {

        System.out.println( "... Key typed: " + e );   }

    public void start() {

      // this.timer.start();

    }

    public void actionPerformed(ActionEvent e) {

      System.out.println( e );                         }

    Shape shape;

    public void paintComponent(Graphics g) {

      for (Shape shape : shapes) shape.draw(g);        }

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

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

    final int BoardWidth = 10;

    final int BoardHeight = 22;

}

import java.awt.*;

 

abstract class Shape {

  int curX = 5, curY = 10;

  Color color;

  World world;

  int coords[][];

  Shape(World world) {

    this.world = world;

  }

  void drawSquare(Graphics g, int x, int y) {

 

        g.setColor(color);

        g.fillRect(x + 1, y + 1,

                   world.squareWidth() - 2, world.squareHeight() - 2);

        g.setColor(color.brighter());

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

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

        g.setColor(color.darker());

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

                   x + world.squareWidth() - 1,

                   y + world.squareHeight() - 1);

        g.drawLine(x + world.squareWidth() - 1,

                   y + world.squareHeight() - 1,

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

   }

 

  void draw(Graphics g) {

        Dimension size = world.getSize();

        int boardTop = (int) size.getHeight() –

                        world.BoardHeight * world.squareHeight();

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

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

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

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

                       boardTop + (world.BoardHeight - y - 1) *

                       world.squareHeight());

        }

  }

 

  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]; }

 

}


 

import java.awt.*;

 

class NoShape extends Shape {

    NoShape(World world) {

    super(world);

    this.color = new Color(0, 0, 0);

    this.coords = new int[][] { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };

  }

}

 

import java.awt.*;

 

class ZShape extends Shape {

  ZShape(World world) {

    super(world);

    this.color = new Color(204, 102, 102);

    this.coords = new int[][] { { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } } ;

  }

}

 

import java.awt.*;

 

class SShape extends Shape {

    SShape(World world) {

    super(world);

    this.color = new Color(102, 204, 102);

    this.coords = new int[][] { { 0, -1 }, { 0, 0 }, { 1, 0 }, { 1, 1 } };

  }

}

 

import java.awt.*;

 

class LineShape extends Shape {

  LineShape(World world) {

    super(world);

    this.color = new Color(102, 102, 204);

    this.coords = new int[][] { { 0, -1 }, { 0, 0 },  { 0, 1 },  { 0, 2 } };

  }

}


 

import java.awt.*;

 

class TShape extends Shape {

  TShape(World world) {

    super(world);

    this.color = new Color(204, 204, 102);

    this.coords = new int[][] { { -1, 0 }, { 0, 0 },  { 1, 0 },  { 0, 1 } };

  }

}

 

import java.awt.*;

 

class SquareShape extends Shape {

   SquareShape(World world) {

    super(world);

    this.color = new Color(204, 102, 204);

    this.coords = new int[][] { { 0, 0 },  { 1, 0 },  { 0, 1 },  { 1, 1 } };

  }

}

 

import java.awt.*;

 

class LShape extends Shape {

  LShape(World world) {

    super(world);

    this.color = new Color(102, 204, 204);

    this.coords = new int[][] { { -1, -1 }, { 0, -1 }, { 0, 0 },  { 0, 1 } };

  } 

}

 

import java.awt.*;

 

class MirroredLShape extends Shape {

    MirroredLShape(World world) {

    super(world);

    this.color = new Color(218, 170, 0);

    this.coords = new int[][] { { 1, -1 }, { 0, -1 }, { 0, 0 }, { 0, 1 } };

  }

}

 


 

Now that we saved all of that let’s see how we can make them move.

I need to tackle the generation and movement of tetraminoes: