I start with

import javax.swing.*; 

class Game extends JFrame {
  Game() {
    World tetris = new World(); 
    this.getContentPane().add( tetris ); 
    this.addKeyListener( tetris ); 
    this.setVisible(true); 
    this.setSize(300, 600); 
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  public static void main(String[] args) {
    Game game = new Game(); 
  }
}

It also needs

import javax.swing.*;
import java.awt.event.*;

class World extends JComponent implements ActionListener, KeyListener {
  Timer timer; // this starts as null
  World() {
    this.timer = new Timer(200, this); 
    this.timer.start(); 
  }
  int tick; 
  public void actionPerformed(ActionEvent e) { 
    this.tick += 1; 
    System.out.println( "Tick: " + tick );    
  }
  public void keyPressed(KeyEvent e) { 
    System.out.println( e ); 
  }
  public void keyReleased(KeyEvent e) { }
  public void keyTyped(KeyEvent e) { }
}

Now I need to add actors in the world. 

Here's in order what we have: 

class Point {
  int x, y;
  Point(int x, int y) {
    this.x = x;
    this.y = y; 
  }
}

The class Point is used in Shape:

import java.awt.*; 

abstract class Shape {
  Point center;
  Color color; 
  Shape(Point center, Color color) {
    this.center = center; 
    this.color = color; 
  }
  abstract void draw(Graphics g);
  void fall() {
    this.center.y += 10;  
  }
}

Here's a specific type of Shape:

import java.awt.*; 

class Circle extends Shape {
  int radius; 
  Circle(Point center, int radius, Color color) {
    super(center, color);
    this.radius = radius;
  }
  public void draw(Graphics g) {
    g.setColor(this.color); 
    g.fillOval(this.center.x - this.radius,
               this.center.y - this.radius,
               2 * this.radius, 
               2 * this.radius);   
    g.setColor(Color.BLACK); 
    g.drawOval(this.center.x - this.radius,
               this.center.y - this.radius,
               2 * this.radius, 
               2 * this.radius);   

  }
}

The World is now able to handle these:

import javax.swing.*;
import java.awt.event.*;
import java.awt.*; 

class World extends JComponent implements ActionListener, KeyListener {
  Timer timer; // this starts as null
  World() {
    this.timer = new Timer(200, this); 
    this.timer.start(); 
  }
  Shape current; // starts as null 
  public void actionPerformed(ActionEvent e) { 
    if (this.current == null) {
      this.current = new Circle( new Point(150, 0), 
                                 10 + (int) (60 * Math.random()), 
                                 new Color( (float) Math.random() , 
                                            (float) Math.random() , 
                                            (float) Math.random() ) 
                               ); 
    } else {
      this.current.fall();  
    }
    this.repaint(); 
  }
  public void keyPressed(KeyEvent e) { 
    System.out.println( e ); 
    if (e.getKeyCode() == 32) 
      this.current = null; 
    this.repaint(); 
  }
  public void keyReleased(KeyEvent e) { }
  public void keyTyped(KeyEvent e) { }
  public void paintComponent( Graphics g ) {
    if (this.current != null) 
      this.current.draw( g ); 
  }
}

Notice that the Game is not changed: 

import javax.swing.*; 

class Game extends JFrame {
  Game() {
    World tetris = new World(); 
    this.getContentPane().add( tetris ); 
    this.addKeyListener( tetris ); 
    this.setVisible(true); 
    this.setSize(300, 600); 
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
  }
  public static void main(String[] args) {
    Game game = new Game(); 
  }
}

So what do we do next? 

(a) clarify the space issue 

http://docs.oracle.com/javase/tutorial/uiswing/events/keylistener.html

(b) add functionality 

... 

Kexin  Mary-Anne Zhichao  Max     Neelan
Erik   Daniel    Kyle     Justin  Artur
Yinan  Yi        Ruifeng  Scott   Adam
Chris  Adam      JSON     Maxine  Yunsheng
Hang   Jordan    Alex     Austin  Kyle
Dylan  Adrian    Justin 

...

 --  perhaps move the shapes left and right 
 --  add and start using a Ground 

import javax.swing.*;
import java.awt.event.*;
import java.awt.*; 

class World extends JComponent implements ActionListener, KeyListener {
  Timer timer; // this starts as null
  World() {
    this.timer = new Timer(200, this); 
    this.timer.start(); 
  }
  Shape current; // starts as null 
  public void actionPerformed(ActionEvent e) { 
    if (this.current == null) {
      this.current = new Circle( new Point(150, 0), 
                                 10 + (int) (60 * Math.random()), 
                                 new Color( (float) Math.random() , 
                                            (float) Math.random() , 
                                            (float) Math.random() ) 
                               ); 
    } else {
      this.current.fall();  
    }
    this.repaint(); 
  }
  public void keyPressed(KeyEvent e) { 
    System.out.println( e ); 
    if (e.getKeyCode() == 32) // the space 
      this.current = null; 
    else if (e.getKeyCode() == KeyEvent.VK_LEFT) // left arrow non-numpad key
      this.current.moveLeft();
    else if (e.getKeyCode() == KeyEvent.VK_RIGHT) // right arrow non-numpad key 
      this.current.moveRight();
    this.repaint(); 
  }
  public void keyReleased(KeyEvent e) { }
  public void keyTyped(KeyEvent e) { }
  public void paintComponent( Graphics g ) {
    if (this.current != null) 
      this.current.draw( g ); 
  }
}

With these changes Shape needs to change too: 

import java.awt.*; 

abstract class Shape {
  Point center;
  Color color; 
  Shape(Point center, Color color) {
    this.center = center; 
    this.color = color; 
  }
  abstract void draw(Graphics g);
  void fall() {
    this.center.y += 10;  
  }
  void moveLeft() {
    this.center.x -= 10;  
  }
  void moveRight() {
    this.center.x += 10;  
  }
}

Now shapes can be moved but they need to be restricted soon.

This is Ground:

import java.util.*; 
import java.awt.*; 

class Ground extends ArrayList<Shape> {
  void draw(Graphics g) {
    for (Shape shape : this) 
      shape.draw( g ); 
  }
}

This the World using a Ground: 

import javax.swing.*;
import java.awt.event.*;
import java.awt.*; 

class World extends JComponent implements ActionListener, KeyListener {
  Timer timer; // this starts as null
  Ground ground;
  World() {
    this.timer = new Timer(200, this); 
    this.timer.start(); 
    this.ground = new Ground(); 
  }
  Shape current; // starts as null 
  public void actionPerformed(ActionEvent e) { 
    if (this.current == null) {
      this.current = new Circle( new Point(150, 0), 
                                 10 + (int) (60 * Math.random()), 
                                 new Color( (float) Math.random() , 
                                            (float) Math.random() , 
                                            (float) Math.random() ) 
                               ); 
    } else {
      this.current.fall();  
      if (this.current.center.y == 400) {
        this.ground.add( this.current ); 
        this.current = null; 
      }
    }
    this.repaint(); 
  }
  public void keyPressed(KeyEvent e) { 
    System.out.println( e ); 
    if (e.getKeyCode() == 32) // the space 
      this.current = null; 
    else if (e.getKeyCode() == KeyEvent.VK_LEFT) // left arrow non-numpad key
      this.current.moveLeft();
    else if (e.getKeyCode() == KeyEvent.VK_RIGHT) // right arrow non-numpad key 
      this.current.moveRight();
    this.repaint(); 
  }
  public void keyReleased(KeyEvent e) { }
  public void keyTyped(KeyEvent e) { }
  public void paintComponent( Graphics g ) {
    if (this.current != null) 
      this.current.draw( g ); 
    this.ground.draw(g); 
  }
}

And now we're ready for the lab.

--