Final Exam tomorrow. 

Write as much as you can from your completed project.

Written exam in lecture, type it in lab, grade it. Self-assessment. 

Attendance ends here, the lab tomorrow can be done from elsewhere. 

If you so want take a picture of your exam and submit your lab by Fri end of day. 

;; block-move : Number Number Block -> Block
(define (block-move dx dy b)
  (make-block (+ dx (block-x b))
              (+ dy (block-y b))
              (block-color b)))

How is this different in some fundamental way from how we do it? 

Today: 

  (a) game ends when the ground is higher than some threshold 

  (b) restrict movement (don't go out, stop when hit ground etc.)

Don't care now about: 

  (c) cleaning up (eliminating) full rows on the ground 

Now here's what we did in class today: 


import java.awt.*; 

public interface World {
  public boolean hasEnded(); 
  public void draw(Graphics g); 
  public void update(); 
  public void keh(String key); 
}

--------------------------------------------------------------------------(            )----------

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

public class Tetris implements World {
  public Tetris() {
    super(); 
    this.current = new Z( 20, 50, 1 ); // just because now I can 
    this.ground = new Ground(); // this will be side-effected
  }
  Tetromino current; 
  Ground ground; 
  public boolean hasEnded() { 

    return false; 
  } 
  public void draw(Graphics g) { 
    this.current.draw(g); 
  }
  private int count;
  public void update() { 
    this.count += 1; 
    System.out.println( count ); 
  }
  public void keh(String key) { 
    if (key.equals("left")) {
      Tetromino future = this.current.moveLeft(); 
      if (this.ground.overlaps( future )) {
        
      } else {
        this.current = future; 
      }
    } else if (key.equals("right")) {
      Tetromino future = this.current.moveRight(); 
      if (this.ground.overlaps( future )) {
        
      } else {
        this.current = future; 
      }
    } else if (key.equals("up")) {
      Tetromino future = this.current.moveUp(); 
      if (this.ground.overlaps( future )) {
        
      } else {
        this.current = future; 
      }
    } else if (key.equals("down")) {
      Tetromino future = this.current.moveDown(); 
      if (this.ground.overlaps( future )) {
        
      } else {
        this.current = future; 
      }
    } else if (key.equals("rotate")) {
      Tetromino future = this.current.rotate(); 
      if (this.ground.overlaps( future )) {
        
      } else {
        this.current = future; 
      }
    } else {
      
    }

  }
  public static void main(String[] args) {
    JFrame a = new JFrame(); 
    a.setSize(200, 500); 
    a.setVisible(true); 
    BigBang b = new BigBang( new Tetris() );
    a.addKeyListener( b ); 
    a.getContentPane().add( b ); 
    b.start(); 
  }
}

--------------------------------------------------------------------------(            )----------


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

public class BigBang extends JComponent
                     implements ActionListener, 
                                KeyListener {
  World world; 
  Timer timer; 
  public BigBang(World w) {
    this.world = w;     
    this.timer = new Timer(1000, this);

  }
  public BigBang(int delay, World w) {
    this.world = w;     
    this.timer = new Timer(delay, this);
     
  }
  public void start() {
    this.timer.start();  
  }
  public void keyPressed(KeyEvent e) { 
    int code = e.getKeyCode(); 
    switch (code) {
      case 37: // left 
               this.world.keh( "left" ); 
               break;
      case 38: // up 
               this.world.keh( "up" ); 
               break;
      case 39: // right
               this.world.keh( "right" ); 
               break;
      case 40: // down 
               this.world.keh( "down" ); 
               break;
      default: // what?!
               this.world.keh( "rotate" );                    
               break; 
    }
    this.repaint(); 
  }
  public void keyReleased(KeyEvent e) { }
  public void keyTyped(KeyEvent e) { }
  public void actionPerformed(ActionEvent e) { 
    this.world.update(); 
    this.repaint(); 
  }
  public void paintComponent(Graphics g) {
    this.world.draw(g);  
  }
}

--------------------------------------------------------------------------(            )----------

import java.awt.*; 

public class Block {
  final static int SIZE = 20; 
  private int x, y, w, h;
  private Color c; 
  public Block(int x, int y, Color c) {
    this.x = x;    
    this.y = y;    
    this.w = Block.SIZE;    
    this.h = Block.SIZE;    
    this.c = c;    
  }
  public String toString() { return "(" + this.x + ", " + this.x + ")" ; }
  public void draw(Graphics g) {
    g.setColor(this.c); 
    g.fillRect(this.x, this.y, this.w, this.h); 
    g.setColor(Color.BLACK); 
    g.drawRect(this.x, this.y, this.w, this.h); 
  }
  public Block moveLeft( ) { // this.x -= Block.SIZE; } 
    return new Block(this.x - Block.SIZE, this.y, this.c); }
  public Block moveRight() { // this.x += Block.SIZE; } 
      return new Block(this.x + Block.SIZE, this.y, this.c); }
  public Block moveUp(   ) { // this.y -= Block.SIZE; } 
    return new Block(this.x, this.y - Block.SIZE, this.c); }
  public Block moveDown( ) { // this.y += Block.SIZE; } 
    return new Block(this.x, this.y + Block.SIZE, this.c); }
}

--------------------------------------------------------------------------(            )----------

import java.util.*; 

public class Ground extends ArrayList<Block> {
  public boolean overlaps(Tetromino t) {
    return false; 
  }
}

--------------------------------------------------------------------------(            )----------

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

public abstract class Tetromino extends ArrayList<Block> {
  protected int x, y; 
  protected int direction; // will take values 0, 1, 2, 3
  public Tetromino(int x, int y, int direction) {
    super(); // since I am in fact an ArrayList of Blocks
    this.x = x; 
    this.y = y; 
    this.direction = direction; 
  }
  public void draw(Graphics g) {
    for (Block b : this)
      b.draw(g); 
  }
  public abstract Tetromino moveLeft(); //  {
    // return new Tetromino
    // this.x -= Block.SIZE;  
    // for (Block b : this) b.moveLeft(); 
  // }
  public abstract Tetromino moveRight(); // {
    // this.x += Block.SIZE; 
    // for (Block b : this) b.moveRight(); 
  // }
  public abstract Tetromino moveUp(); // {
    // this.y -= Block.SIZE;  
    // for (Block b : this) b.moveUp(); 
  // } 
  public abstract Tetromino moveDown(); // {
    // this.y += Block.SIZE;  
    // for (Block b : this) b.moveDown(); 
  // }
  public abstract Tetromino rotate(); 
}

--------------------------------------------------------------------------(            )----------

import java.awt.*; 

public class T extends Tetromino {
  
  final static int[][][] shape = 
  {
    { { 0, 1, 0 },
      { 1, 1, 1 },
      { 0, 0, 0 }
    }, // index 0 in the shape array 
    { { 0, 1, 0 },
      { 0, 1, 1 },
      { 0, 1, 0 }
    }, // index 0 in the shape array 
    { { 0, 0, 0 },
      { 1, 1, 1 },
      { 0, 1, 0 }
    }, // index 0 in the shape array 
    { { 0, 1, 0 },
      { 1, 1, 0 },
      { 0, 1, 0 }
    }, // index 0 in the shape array 
        
  }; 
  
  public Tetromino moveLeft( ) { return new T(this.x - Block.SIZE, 
                                              this.y, 
                                              this.direction ); }
  public Tetromino moveRight() { return new T(this.x + Block.SIZE, 
                                              this.y, 
                                              this.direction ); }
  public Tetromino moveUp(   ) { return new T(this.x, 
                                              this.y - Block.SIZE, 
                                              this.direction ); }
  public Tetromino moveDown( ) { return new T(this.x, 
                                              this.y + Block.SIZE, 
                                              this.direction ); }
  
  public Tetromino rotate() {
    // this.direction += 1; // reminder a op= b; is the same as a = a op b;  
    // this.direction %= 4; 
    int newDirection = (this.direction + 1) % 4; 
    // // what else 
    // this.reset(); 
    return new T(this.x, this.y, newDirection); 
  }
  
  private void reset() {
    // this.clear(); 
    for (int i = 0; i < shape[direction].length; i++) 
      for (int j = 0; j < shape[direction][i].length; j++) 
        if (shape[direction][i][j] == 1)
          this.add( new Block( this.x + Block.SIZE * j, 
                               this.y + Block.SIZE * i, 
                               Color.YELLOW
                             ) 
                  );      
  }
  
  public T(int x, int y, int d) {
    super(x, y, d); 
    this.reset();             
  }
}

--------------------------------------------------------------------------(            )----------

import java.awt.*; 

public class Z extends Tetromino {
  
  final static int[][][] shape = 
  {
    { { 1, 1, 0 },
      { 0, 1, 1 },
      { 0, 0, 0 }
    }, // index 0 in the shape array 
    { { 0, 0, 1 },
      { 0, 1, 1 },
      { 0, 1, 0 }
    }, // index 0 in the shape array 
    { { 0, 0, 0 },
      { 1, 1, 0 },
      { 0, 1, 1 }
    }, // index 0 in the shape array 
    { { 0, 1, 0 },
      { 1, 1, 0 },
      { 1, 0, 0 }
    }, // index 0 in the shape array 
        
  }; 
  
  public Tetromino moveLeft( ) { return new Z(this.x - Block.SIZE, 
                                              this.y, 
                                              this.direction ); }
  public Tetromino moveRight() { return new Z(this.x + Block.SIZE, 
                                              this.y, 
                                              this.direction ); }
  public Tetromino moveUp(   ) { return new Z(this.x, 
                                              this.y - Block.SIZE, 
                                              this.direction ); }
  public Tetromino moveDown( ) { return new Z(this.x, 
                                              this.y + Block.SIZE, 
                                              this.direction ); }
  
  public Tetromino rotate() {
    // this.direction += 1; // reminder a op= b; is the same as a = a op b;  
    // this.direction %= 4; 
    int newDirection = (this.direction + 1) % 4; 
    // // what else 
    // this.reset(); 
    return new Z(this.x, this.y, newDirection); 
  }
  
  private void reset() {
    // this.clear(); 
    for (int i = 0; i < shape[direction].length; i++) 
      for (int j = 0; j < shape[direction][i].length; j++) 
        if (shape[direction][i][j] == 1)
          this.add( new Block( this.x + Block.SIZE * j, 
                               this.y + Block.SIZE * i, 
                               Color.YELLOW
                             ) 
                  );      
  }
  
  public Z(int x, int y, int d) {
    super(x, y, d); 
    this.reset();             
  }
}

--------------------------------------------------------------------------(            )----------