Source files:

Tetris.java
import javax.swing.JFrame;

public class Tetris {
	public static void main(String[] args) {
	      JFrame window = new JFrame("Tetris Game");
	      TetrisPanel content = new TetrisPanel();
	      window.setContentPane(content);
	      window.setSize(215, 435);
	      window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
	      window.setResizable(false);  
	      // User can't change the window's size.
	      window.setVisible(true);
	   }
}
							
TetrisApplet.java
import javax.swing.JApplet;

public class TetrisApplet extends JApplet {
	// Start the applet
	public void init() {
		setContentPane( new TetrisPanel() );
	}
}
							
TetrisPanel.java
import java.awt.*;        
import java.awt.event.*;
import java.util.Random;

import javax.swing.*;

/**
 * This panel implementsis a simple tetris game.
 */

public class TetrisPanel extends JPanel {
	
	private Timer timer;
	private int height, width;
	
	boolean[][] mesh;	// the Tetris field mesh
	int middle=5;		// the middle of the new element
	private Stone stone;		// new stone object
	Random generator = new Random();
	int randomStone = this.generator.nextInt(7) + 1;
	
	public TetrisPanel() {
	// The constructor
		setBackground(Color.WHITE);
		
		ActionListener action = new ActionListener() {
		// Defines the action taken each time the timer fires.
			public void actionPerformed(ActionEvent evt) {
				moveDown();
				repaint();
			}
		};
	      
		timer = new Timer( 150, action );  // Fires every 100 milliseconds.	

		addMouseListener( new MouseAdapter() {
		// The mouse listener simply requests focus when the user
		// clicks the panel.
			public void mousePressed(MouseEvent evt) {
				requestFocus();
			}
		} );

		addFocusListener( new FocusListener() {
		// The focus listener starts the timer when the panel gains
		// the input focus and stops the timer when the panel loses
		// the focus.  It also calls repaint() when these events occur.
			public void focusGained(FocusEvent evt) {
				timer.start();
				repaint();
			}
			public void focusLost(FocusEvent evt) {
				timer.stop();
				repaint();
			}
	      } );
	      
		addKeyListener( new KeyAdapter() {
		// The key listener responds to keyPressed events on the panel. Only
		// the left-, right-, down-, and up-arrow keys have any effect.  The left- and
		// right-arrow keys move the stone while up-arrow is to rotate,
		// down-arrow move the block fast to the bottom.
			public void keyPressed(KeyEvent evt) {
			// This routine is called each time the user presses
	    	// a key on the keyboard (while the applet has the keyboard
	    	// focus).  The value of evt.getKeyCode() tells which
			// key was pressed.  For this applet, only the three of the
			// arrow keys will have any effect.  The codes for the
			// four arrow keys are KeyEvent.VK_UP, KeyEvent.VK_DOWN,
			// KeyEvent.VK_LEFT, and KeyEvent.VK_RIGHT.

				int code = evt.getKeyCode();  // which key was pressed

				if (code == KeyEvent.VK_LEFT) {
				// Move the stone left.  (If the move is not posible, the stone 
				// will stay where it is, ot the position will be adjusted
				// in the stone.stoneMoveLeft(boolean[][]) method.)
					stone = stone.stoneMoveLeft(mesh);
				}
				else if (code == KeyEvent.VK_RIGHT) {  
				// Move the stone right.  (If the move is not posible, the stone 
				// will stay where it is, ot the position will be adjusted
				// in the stone.stoneMoveRight(boolean[][]) method.)
					stone = stone.stoneMoveRight(mesh);
				}
				else if (code == KeyEvent.VK_DOWN) {
				// move the stone fast down
					moveDown();
				}
				else if (code == KeyEvent.VK_UP){
				// rotate the stone counterclockwise
					stone = stone.stoneRotateCounterClockwise(mesh, randomStone);
				}
	    	 
			} // end keyPressed()
		} );

	} // end constructor
	      
	 	
	public boolean fullLine(boolean[][] mesh, int y){
	// test if a y line is full	
		for(int x=0; x < mesh[y].length; x++){
			if(!mesh[y][x]){
				return false;
			}
		}
		return true;
	} // end fullLine
	 	
	public boolean[][] removeLine(boolean[][] mesh){
		if(fullLine(mesh, 0)){
			// if the top line is full, remove it
			for(int x=0; x < mesh[0].length; x++){
				mesh[0][x]=false;
			}
		}
		for(int l=1; l < mesh.length; l++){
			// go trough all lines to the bottom exept the top one
			if(fullLine(mesh, l)){ // if the line is full
				for(int y=l; y>0; y--){
					// remove the line (replace with the line over it)
					for(int x=0; x < mesh[y].length; x++){
						mesh[y][x] = mesh[y-1][x];
					}
				}
				for(int x=0; x < mesh[0].length; x++){
					mesh[0][x]=false;
				}
			}
		}
		return mesh;
	}
	
	public void moveDown(){
		if(this.stone.stoneCollision(this.mesh)){
			this.mesh = this.stone.updateMesh(this.mesh);
			this.mesh = removeLine(this.mesh);
			// Pick a random number from 1 to 7, which represents a stone 
			this.randomStone = this.generator.nextInt(7) + 1;
			/*
			 * if(this.stone.isThePlayerDead(this.mesh, this.middle, this.randomStone)){
			 * this.stone = this.stone.newStone(this.middle, this.randomStone);
			 * this.mesh = this.stone.updateMesh(this.mesh);
			 * this.stone.stoneDraw(this.theCanvas, this.mesh);
			 * this.theCanvas.close();
			 * this.endOfWorld("Du bist tot :-)");
			 * } else
			 * this.stone = this.stone.newStone(this.middle, this.randomStone);
	 		 * //Testen ob der Spielser tot ist
	 		 */		
			this.stone = this.stone.newStone(this.middle, this.randomStone);
		} else {
			this.stone = this.stone.stoneDrop();
		}
	}
	
	public void draw(Graphics g){
		// draw the hole thing
		this.stone.stoneDraw(g, this.mesh);
	}
	
	/**
	 * The paintComponent() method draws the current state of the game.  It
	 * draws a gray or cyan border around the panel to indicate whether or not
	 * the panel has the input focus.  It draws the stone and calling draw() method.
	 * 
	 */
	public void paintComponent(Graphics g) {
		
		super.paintComponent(g);  // Fill panel with background color, white.
		
		if (stone == null){	// initialize the begin components of the game
			width = getWidth();
	        height = getHeight();
			stone = new Stone(middle);
			stone = stone.newStone(middle, randomStone);
			mesh = new boolean[20][10];
			for(int i=0; i < mesh.length; i++){
				for(int j=0; j < mesh[i].length; j++){
					mesh[i][j]=false;
				}
			}
		}
	         	         
		if (hasFocus())
			g.setColor(Color.CYAN);
		else {
	            g.setColor(Color.RED);
	            g.drawString("click to start", 65, 190);
	            g.setColor(Color.GRAY);
	         }
	         g.drawRect(0,0,width-1,height-1);
	         g.drawRect(1,1,width-3,height-3);
	         g.drawRect(2,2,width-5,height-5);
	         
	         draw(g);
	         
	      } // end drawFrame()
	
	// Nested class Stone
	private class Stone {	
		Block[] block; // Array with 4 blocks (stone)
		Block b; // Block
		
		public Stone(int middle){
		// constructor for new block in the middle of the Field
			this.b = new Block(middle, 0);
		} // end constructor
		
		public Stone(Block block1, Block block2, Block block3, Block block4){
		// constructor for stone (4 blocks)
			this.block = new Block[4];
			this.block[0] = block1;
			this.block[1] = block2;
			this.block[2] = block3;
			this.block[3] = block4;
		} // end Stone()
		
		public Stone newStone(int middle, int n){
		// Create new Stone
		// n - random number of stone type
			Block block2 = new Block(middle, 0);
			Block block1 = new Block(block2.getX(), block2.getY()+1);
			Block block3 = new Block(0,0);
			Block block4 = new Block(0,0);
			switch(n){
				case 1:
					block3 = new Block(block1.getX()+1, block1.getY());
					block4 = new Block(block1.getX()+1, block1.getY()+1);
					break;
				case 2:
					block3 = new Block(block1.getX()-1, block1.getY());
					block4 = new Block(block1.getX()-1, block1.getY()+1);
					break;
				case 3:
					block3 = new Block(block1.getX()-1, block1.getY());
					block4 = new Block(block1.getX()+1, block1.getY());
					break;
				case 4:
					block3 = new Block(block1.getX(), block1.getY()+1);
					block4 = new Block(block1.getX()+1, block1.getY()+1);
					break;
				case 5:
					block3 = new Block(block1.getX(), block1.getY()+1);
					block4 = new Block(block1.getX()-1, block1.getY()+1);
					break;
				case 6:
					block3 = new Block(block2.getX()-1, block2.getY());
					block4 = new Block(block1.getX()-1, block1.getY());
					break;
				case 7:
					block3 = new Block(block1.getX(), block1.getY()+1);
					block4 = new Block(block1.getX(), block1.getY()+2);
					break;
			    default:
			    	block3 = new Block(block1.getX(), block1.getY()+1);
			    	block4 = new Block(block1.getX(), block1.getY()+2);
			    	break;
			    }
			    Stone newstone = new Stone (block1, block2, block3, block4);
			    return newstone;
		} // end newStone()
		
		public boolean stoneCollision(boolean[][] mesh){
		// Bottom collision detection
			for(int i=0; i < this.block.length; i++){
				// to trough every block
				// and test it if the down move is posible
				if(this.block[i].collision(mesh)){	
					return true;
				}
			}
			return false;
		} // end stoneCollision()
		
		public boolean[][] updateMesh(boolean[][] mesh){
		// mark the cells in the mesh as occupied
			for(int i=0; i < this.block.length; i++){
				mesh[this.block[i].getY()][this.block[i].getX()]=true;
			}
			return mesh;
		} // end updateMesh
		
		public Stone stoneDrop(){
		// move the stone downwards
			for(int i=0; i < this.block.length; i++){
				this.block[i] = this.block[i].drop();
			}
			return new Stone(block[0], block[1], block[2], block[3]);
		} // end stoneDrop
		
		public Stone stoneMoveLeft(boolean[][] mesh){
		// move the stone left if it is posible
			for(int i=0; i < this.block.length; i++){
				// if one of the blocks is not left moveable
				if(!this.block[i].isMoveableLeft(mesh)){
					// return the old stone
					return new Stone(block[0], block[1], block[2], block[3]);
				}
			}
			for(int i=0; i < this.block.length; i++){
				this.block[i] = this.block[i].blockMoveLeft();
			}
			return new Stone(block[0], block[1], block[2], block[3]);
		} // end stoneMoveLeft
		
		public Stone stoneMoveRight(boolean[][] mesh){
			// move the stone right if it is posible
				for(int i=0; i < this.block.length; i++){
					// if one of the blocks is not right moveable
					if(!this.block[i].isMoveableRight(mesh)){
						// return the old stone
						return new Stone(block[0], block[1], block[2], block[3]);
					}
				}
				for(int i=0; i < this.block.length; i++){
					this.block[i] = this.block[i].blockMoveRight();
				}
				return new Stone(block[0], block[1], block[2], block[3]);
			} // end stoneMoveRight
		

		public boolean isTurnableCounterClockwise (boolean[][] mesh, Block centerBlock){
		// Checks if it's allowed to rotate the block (this)
		// by 90 degrees counterclockwise around centerBlock.
		// the method is used in stoneRotateCounterClockwise()
			for(int i=0; i < this.block.length; i++){
				// rotate a this.block[i] around centerBlock
				Block testBlock = this.block[i].rotateCounterClockwise(centerBlock);
				if(!testBlock.isAValidMove(mesh)){ 
				// if the result block is in a collision
					return false;	// return false
				}
		     }
			return true; 
			// return true if neither of the blocks in the stone are in collision
		} // end isTurnableCounterClockwise

		boolean isTurnableClockwise (boolean[][] mesh, Block centerBlock){
		// Checks if it's allowed to rotate the block (this)
		// by 90 degrees counterclockwise around centerBlock.
		// the method is used in stoneRotateCounterClockwise()
			for(int i=0; i < this.block.length; i++){
				//rotate a this.block[i] around centerBlock
				Block testBlock = this.block[i].rotateClockwise(centerBlock);
				if(!testBlock.isAValidMove(mesh)){
				// if the result block is in a collision
					return false;	// return false
				}
			}
			return true;
			// return true if neither of the blocks in the stone are in collision
		} // end is TurnableClockwise
		     
		  
		Stone stoneRotateCounterClockwise(boolean[][] mesh, int randomStone){
		// turn the stone left
			for(int i=0; i < this.block.length; i++){
			// test every block in the stone as turningpoint
				// See isTurnableCounterClockwise.
				if(isTurnableCounterClockwise(mesh, this.block[i]) && randomStone != 6){
				// if the turn of the sotne is possible and
				// the stone is not a quadrat (quadrat do not rotate)
					for (int j=0; j < this.block.length; j++){
					// rotate all the blocks in the stone
						this.block[j] = this.block[j].rotateCounterClockwise(this.block[i]);
					}
					// and return the new stone
					return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
				}
				// if a collision is detected, test anader block in the stone as turningpoint 
		     }
			// if no block can be used as a turningpoing, return the old stone
			return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
		} // end stoneRotateCounterClockwise()
		
		Stone stoneRotateClockwise(boolean[][] mesh, int randomStone){
		// turn the stone right
			for(int i=0; i < this.block.length; i++){
			// test every block in the stone as turningpoint
				// See isTurnableClockwise.
				if(isTurnableClockwise(mesh, this.block[i]) && randomStone != 6){
					// if the turn of the sotne is possible and
					// the stone is not a quadrat (quadrat do not rotate)
					for (int j=0; j < this.block.length; j++){
						// rotate all the blocks in the stone
						this.block[j] = this.block[j].rotateClockwise(this.block[i]);
					}
					// and return the new stone
					return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
				}
				// if a collision is detected,
				// test another block in the stone as turningpoint 
			}
			// if no block can be used as a turningpoing, return the old stone
			return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
		} // end stoneRotateClockwise()
		
		public void stoneDraw(Graphics g, boolean[][] mesh){
		// draw the stone
			for(int i=0; i < this.block.length; i++){
			// draw every block from the stone
				this.block[i].drawFrame(g, mesh);
			}
		} // end stoneDraw

		// Nested class Block
		private class Block {
			int x;
			int y;
			int DELTA = 1;
			int SIZE = 20;
			
			public Block(int x, int y){
				this.x = x;
				this.y = y;
			} // Constructor
			
			public int getX(){	// Method whitch returns X Block cordinate
				return this.x;
			}
			
			public int getY(){	// Method whitch returns Y Block cordinate
				return this.y;
			}
			
			public int meshToPixels(int i){
			// Method whitch returns the real pixel cordinates 
				return i = i * this.SIZE + 3;
			}
			
			public boolean collision(boolean[][] mesh){	// Collision detection
				if(this.y >= mesh.length-1) // If the block has reached the floor
					return true;
				else if(mesh[this.y+this.DELTA][this.x]) //if there is a block under
					return true;
				else
					return false;
			}
			
			public Block drop(){ // The next move of the block
				return new Block(this.x, this.y + this.DELTA);
			}
			
			// Create a virtual block and test if it's posible to exists
			// If the virtual block is in no collision condition, then it becomes real
			public boolean isAValidMove(boolean[][] mesh) {
				if(this.y > mesh.length-1 ||		 //
				   this.x > mesh[this.y].length-1 || // The block is out of bound
				   this.x  <  0 ||					 //
				   mesh[this.y][this.x])			 // The block is over another block
					return false;
				return true;
			}
			
			// The functionality of the method should be integrated in isAValidMove.
			// The method is still.
			public boolean isMoveableLeft(boolean[][] mesh){
				if(this.x > 0 && mesh[this.y][this.x-this.DELTA])
				// if a block exists from left
					return false;
				else if(this.x  < = 0)	// if the block is out of bound from left
					return false;
				else
					return true;
			}
			
			public Block blockMoveLeft(){	// Create block on left
				return new Block(this.x - this.DELTA, this.y);
			}
			
			// The functionality of the method should be integrated in isAValidMove.
			public boolean isMoveableRight(boolean[][] mesh){
				if(this.x  <  mesh[this.y].length-1 && mesh[this.y][this.x+this.DELTA])
				// if a block exists from right 
					return false;
				else if(this.x >= mesh[this.y].length-1)
				// if the block is out of bound from right
					return false;
				else
					return true;
			}
			
			public Block blockMoveRight(){ // Create block on right
				return new Block(this.x + this.DELTA, this.y);
			}
			
			//Drehen ein Block rechts
			// Achtung: Nicht anfassen
			// Rotates the point (this) clockwise by 90 degrees
			// around the parameter (that).
			public Block rotateClockwise(Block that){
				return new Block(that.x+(this.y-that.y), that.y-(this.x-that.x));
			}
			 
			// Derhen ein Block links
			// Achtung: Nicht anfassen
			// Rotates the point (this) counter clockwise by 90 degrees
			// around the parameter (that).
			public Block rotateCounterClockwise(Block that){
				return new Block(that.x-(this.y-that.y), that.y+(this.x-that.x));
			}
			
			synchronized public void drawFrame(Graphics g, boolean[][] mesh) {
		        // This routine is called to draw the next frame of the animation.
		        // The parameter, g, is a graphics context for drawing the frame.
		        // The parameter mesh, is the boolean array whith the saved block.
		        // Here, methods are called to process and draw each of the
		        // three objects in the scene.
				// First, fill the whole drawing area with green
				g.setColor(Color.red);
				for(int y=0; y  <  mesh.length; y++){
					for(int x=0; x  <  mesh[y].length; x++){
						if(mesh[y][x]){
							g.fillRect(meshToPixels(x), meshToPixels(y), this.SIZE, this.SIZE);
						}
					}
				}
				g.fillRect(meshToPixels(this.x), meshToPixels(this.y), this.SIZE, this.SIZE);
			}
		} // end Block class
	} // end Stone class
}
							
tetris.scm
import idraw.*;
import geometry.*;
import colors.*;
import java.util.Random;

class Field extends World{
  AColor BACKGROUND = new White();
  Stone stone;
  int width;
  int height;
  int middle;
  double time;
  boolean[][] mesh;
  Random generator = new Random();
  int randomStone = this.generator.nextInt(7) + 1;
 
  Field(int width, int height, double time){
    this.width = width;
    this.height = height;
    this.middle = width/2;
    this.stone = new Stone(this.middle);
    this.stone = this.stone.newStone(this.middle, this.randomStone);
    this.time = time;
    this.bigBang(this.stone.stoneMeshToPixels(this.width), this.stone.stoneMeshToPixels(this.height), this.time);
    mesh = new boolean[20][10];
    for(int i=0; i < mesh.length; i++){
       for(int j=0; j < mesh[i].length; j++){
          mesh[i][j]=false;
       }
     }
  }  
  
  //change the world, i.e. move the Block downwards
 synchronized public void onTick(){
     moveDown();
  }
  
      // Testen ob eine Linie voll ist
  public boolean lineFull(boolean[][] mesh, int y){
     for(int x=0; x < mesh[y].length; x++){
       if(!mesh[y][x]){   // ob eine Stelle in der Linie false ist
          return false;          // liefern false
        }
     }
     return true;
  }
  
  // Loeschen eine Linie
  public boolean[][] removeLine(boolean[][] mesh){
    if(lineFull(mesh, 0)){
        for(int x=0; x < mesh[0].length; x++)
          mesh[0][x] = false;
    }
    // Probieren jede Zeile ob die voll ist?
    // wenn ja
    // uberschreibe jede Zeile mit der oberen Zeile bis zum ganz oben
    for(int l=1; l < mesh.length; l++){
      if(lineFull(mesh, l)){
        for(int y=l; y>=1; y--){
          for(int x=0; x < mesh[y].length; x++){
            mesh[y][x] = mesh[y-1][x];
          }
        }
        for(int x=0; x < mesh[0].length; x++){
          mesh[0][x] = false;
        }
      }
    }
    return mesh;
  }
  
  public void moveDown(){
    if(this.stone.stoneCollision(this.mesh)){
      this.mesh = this.stone.updateMesh(this.mesh);
      this.mesh = removeLine(this.mesh);
      this.randomStone = this.generator.nextInt(7) + 1;
      // Waehlen eine zufahle Zahl von 1 bis 7, die entspricht das Blocknummer
      if(this.stone.isThePlayerDead(this.mesh, this.middle, this.randomStone)){
        this.stone = this.stone.newStone(this.middle, this.randomStone);
        this.mesh = this.stone.updateMesh(this.mesh);
        this.stone.stoneDraw(this.theCanvas, this.mesh);
        this.theCanvas.close();
        this.endOfWorld("Du bist tot :-)");
      } else
        this.stone = this.stone.newStone(this.middle, this.randomStone);
      // Testen ob der Spielser tot ist
    } else {
      this.stone = this.stone.stoneDrop();
    }
  }

  
  //draw the world, do not change the world here
  public void draw(){
    this.stone.stoneDraw(this.theCanvas, this.mesh);
  }

  //change zhe world
 synchronized public void onKeyEvent(String Key){
    if (Key.equals("q")){
      this.theCanvas.close();
      this.endOfWorld("Spiel zuende!");
    }
    else{
      if (Key.equals("a")){
        this.stone = this.stone.stoneMoveLeft(this.mesh);
      } else if (Key.equals("d")) {
          this.stone = this.stone.stoneMoveRight(this.mesh);
      } else if (Key.equals("s")){
          moveDown();
      } else if (Key.equals("w")){
          this.stone = this.stone.stoneRotateCounterClockwise(this.mesh, this.randomStone);
      } else if (Key.equals("e")){
          this.stone = this.stone.stoneRotateClockwise(this.mesh, this.randomStone);
      } else {}
    }
  }
}

class Stone{
  Block[] block;
  Block b;
  
  Stone(int middle){
    this.b = new Block(middle, 0);
  }
  
  Stone(Block block1, Block block2, Block block3, Block block4){
    this.block = new Block[4];
    this.block[0]= block1;
    this.block[1]= block2;
    this.block[2]= block3;
    this.block[3]= block4;
  }
  
  // Erzeugen neues Stein
  Stone newStone(int middle, int n){ // n - random number of the stone, w - the weith of the mesh
    Block block2 = new Block(middle, 0);                      // Die Numerierung ist vertauscht um
    Block block1 = new Block(block2.getX(), block2.getY()+1); // die Rotation zu beguenstigt
    Block block3 = new Block(0,0);
    Block block4 = new Block(0,0);
    if(n == 1){
      block3 = new Block(block1.getX()+1, block1.getY());
      block4 = new Block(block1.getX()+1, block1.getY()+1);
    } else if (n == 2){
      block3 = new Block(block1.getX()-1, block1.getY());
      block4 = new Block(block1.getX()-1, block1.getY()+1);
    } else if (n == 3){
      block3 = new Block(block1.getX()-1, block1.getY());
      block4 = new Block(block1.getX()+1, block1.getY());
    } else if (n == 4){
      block3 = new Block(block1.getX(), block1.getY()+1);
      block4 = new Block(block1.getX()+1, block1.getY()+1);
    } else if (n == 5){
      block3 = new Block(block1.getX(), block1.getY()+1);
      block4 = new Block(block1.getX()-1, block1.getY()+1);
    } else if (n == 6){
      block3 = new Block(block2.getX()-1, block2.getY());
      block4 = new Block(block1.getX()-1, block1.getY());
    } else if (n == 7){
      block3 = new Block(block1.getX(), block1.getY()+1);
      block4 = new Block(block1.getX(), block1.getY()+2);
    } else {
      block3 = new Block(block1.getX(), block1.getY()+1);
      block4 = new Block(block1.getX(), block1.getY()+2);
    }
    Stone newstone = new Stone (block1, block2, block3, block4);
    return newstone;
  }
  int stoneMeshToPixels(int i){
    return block[0].meshToPixels(i);
  }
  // Testen ob die Elementen den obesten Rand erreicht werden
  boolean isThePlayerDead(boolean[][] mesh, int middle, int n){
    Stone newstone = newStone(middle, n);
    for(int i=0; i < newstone.block.length; i++){
      if(!newstone.block[i].isAValidMove(mesh))
         return true;
    }
    return false;
  }

  boolean stoneCollision(boolean[][] mesh){
    for(int i=0; i < this.block.length; i++){
      if (this.block[i].collision(mesh))
         return true;
    }
    return false;
  }
  
  // Aktualisierung das Mesh wenn Kollision getroffen wird.
 synchronized boolean[][] updateMesh(boolean[][] mesh){
    for(int i=0; i < this.block.length; i++)
       mesh[this.block[i].getY()][this.block[i].getX()]=true;
    return mesh;
  }
  
  synchronized Stone stoneDrop(){
    for(int i=0; i < this.block.length; i++)
       this.block[i] = this.block[i].drop();
    return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
  }
  synchronized Stone stoneMoveLeft(boolean[][] mesh){
    for(int i=0; i < this.block.length; i++){
      if(!this.block[i].isMoveableLeft(mesh))
         return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
    }
    for(int i=0; i < this.block.length; i++){
      this.block[i] = this.block[i].moveLeft();
    }
    return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
  }
  synchronized Stone stoneMoveRight(boolean[][] mesh){
    for(int i=0; i < this.block.length; i++){
      if(!this.block[i].isMoveableRight(mesh))
         return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
    }
    for(int i=0; i < this.block.length; i++){
      this.block[i] = this.block[i].moveRight();
    }
    return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
  }
  
  // Checks if it's allowed to rotate the block (this) by 90 degrees counterclockwise around b.
  boolean isTurnableCounterClockwise (boolean[][] mesh, Block b){
    for(int i=0; i < this.block.length; i++){
       // See isTurnableCounterClockwise.
       Block testBlock = this.block[i].rotateCounterClockwise(b);
       if(!testBlock.isAValidMove(mesh))
          return false;
     }
     return true;
  }
  
  // Checks if it's allowed to rotate the block (this) by 90 degrees clockwise around b.
  boolean isTurnableClockwise (boolean[][] mesh, Block b){
    for(int i=0; i < this.block.length; i++){
       // See isTurnableCounterClockwise.
       Block testBlock = this.block[i].rotateClockwise(b);
       if(!testBlock.isAValidMove(mesh))
          return false;
     }
     return true;
  }
     
  
  // Drehen das Stein links
  Stone stoneRotateCounterClockwise(boolean[][] mesh, int randomStone){
     for(int i=0; i < this.block.length; i++){ // Testen jedes Stein als Turningpoint
       // See isTurnableCounterClockwise.
       if(isTurnableCounterClockwise(mesh, this.block[i]) && randomStone != 6){
           for (int j=0; j < this.block.length; j++){
             this.block[j] = this.block[j].rotateCounterClockwise(this.block[i]);
           }
           return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
       }  
     }
     return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
   }
  
  // Drehen das Stein rechts
  Stone stoneRotateClockwise(boolean[][] mesh, int randomStone){
     for(int i=0; i < this.block.length; i++){ // Testen jedes Stein als Turningpoint
       // See isTurnableClockwise.
       if(isTurnableClockwise(mesh, this.block[i]) && randomStone != 6){
           for (int j=0; j < this.block.length; j++){
             this.block[j] = this.block[j].rotateClockwise(this.block[i]);
           }
           return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
       }  
     }
     return new Stone(this.block[0], this.block[1], this.block[2], this.block[3]);
  }
  
 synchronized public void stoneDraw(Canvas c, boolean[][] mesh){
    for(int i=0; i < this.block.length; i++)
       this.block[i].draw(c, mesh);
  }
}

class Block{
  int x;
  int y;
  int DELTA = 1;
  int SIZE = 20;
  
  Block (int x, int y){
    this.x = x;
    this.y = y;
  }
  
  int getX(){
    return this.x;
  }
  int getY(){
    return this.y;
  }
  
 int meshToPixels(int i){
    return i = i*this.SIZE;
  }
  
  // Collision detection
 synchronized boolean collision(boolean[][] mesh){
    if(this.y  <  mesh.length-1 && mesh[this.y+this.DELTA][this.x]){ // wenn unter ein Element existiert
      return true;
    } else if(this.y >= mesh.length-1){  // Collision mit dem Boden
      return true;
    } else {return false;}
  }
  
 synchronized Block drop(){
      return new Block(this.x, this.y + this.DELTA);
  }
  
    // Probier ob eine block in eine Collision steht
  // Normalleweise wird eine TestBlock erzeugen und getestet.
  // Wenn das position ok ist, dann wird das TestBlock als neues Block genommen.
  boolean isAValidMove(boolean[][] mesh){
    if (this.y > mesh.length-1 ||         //
        this.x > mesh[this.y].length-1 || // Ob das Block im Mesh liegt
        this.x  <  0 ||                     //
        mesh[this.y][this.x])             // Ob das Block auf einen anderen Blockliegt
      return false;
    return true;
  }
  
  // Diese Funktion muss ersetzt mit isAValidMove werden. (die Funktion ist noch benutzt)
  boolean isMoveableLeft(boolean[][] mesh){
    if(this.x > 0 && mesh[this.y][this.x-DELTA]){  // wenn von Links ein Element existiert
      return false;
    } else if (this.x  < = 0){  // Collision mit den linken Rand
      return false;
    } else {
      return true;
    }
  }
  
  synchronized Block moveLeft(){
      return new Block(this.x - this.DELTA, this.y);
  }
  
  // Diese Funktion muss ersetzt mit isAValidMove werden. (die Funktion ist noch benutzt)
  boolean isMoveableRight(boolean[][] mesh){
    if(this.x  <  mesh[this.y].length-1 && mesh[this.y][this.x+DELTA]){  // wenn von Rechts ein Element existiert
      return false;                               // return false;
    } else if (this.x >= mesh[this.y].length-1){  // Collision mit dem rechten Rand
      return false;
    } else {
      return true;
    }
  }
 synchronized Block moveRight(){
    return new Block(this.x + this.DELTA, this.y);
  }
  
  // Drehen ein Block rechts
  // Achtung: Nicht anfassen
  // Rotates the point (this) clockwise by 90 degrees around the parameter (that).
  synchronized public Block rotateClockwise(Block that){
		return new Block(that.x+(this.y-that.y), that.y-(this.x-that.x));
	}
 
  // Derhen ein Block links
  // Achtung: Nicht anfassen
  // Rotates the point (this) counter clockwise by 90 degrees around the parameter (that).
	synchronized public Block rotateCounterClockwise(Block that){
		return new Block(that.x-(this.y-that.y), that.y+(this.x-that.x));
	}
  
  // zeichnen das Stern
 synchronized void draw(Canvas c, boolean[][] mesh){
     for(int y=0; y < mesh.length; y++){
       for(int x=0; x < mesh[y].length; x++){
          if(mesh[y][x]){  //Zeichnen alle Elementen in mesh Array die true sind
            c.drawRect(new Posn(meshToPixels(x), meshToPixels(y)), this.SIZE, this.SIZE, new Red());
          }
       }
     }  // Zeichnen dem bewegenden Block
     c.drawRect(new Posn(meshToPixels(this.x), meshToPixels(this.y)), this.SIZE, this.SIZE, new Red());  
    }
}