Tetris

I created a Tetris clone in Java.
These are the controls for the applet below:

← and → Move brick left or right 1 position
Ctrl-← and Ctrl-→ Move brick left or right as far as possible
Shift Turn brick clockwise
Turn brick counterclockwise
Move brick down 1 position
CTRL ↓ of Enter Move brick down as far as possible


Here is the source code:

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

@SuppressWarnings("serial")
public class TetrisApplet extends Applet implements Observer
{
	final static String startTxt = "Start!",
					    pauseTxt = "Pause";

	IntLabel score, level, lines, tetrisses;
	TetrisGrid g;
	TetrisCanvas c;
    AudioClip musicA, musicB, musicC;
    JRadioButton radioA, radioB, radioOff;

	public void init()
	{
		this.setLayout(new BorderLayout());
		Panel panel = new Panel();
		panel.setLayout(new GridLayout(2,1));
		this.setBackground(Color.darkGray);
		this.setSize(310,400);
		g = new TetrisGrid(10, 20, 20);
		g.addObserver(this);
		c = new TetrisCanvas();
		Panel nextPanel = new Panel(new BorderLayout());
		NextViewer n = new NextViewer(g);
		JButton b = new JButton(new AbstractAction(startTxt) {
            public void actionPerformed(ActionEvent e) { c.pressStart(); }
        });
		JButton p = new JButton(new AbstractAction(pauseTxt) {
            public void actionPerformed(ActionEvent e) { c.pressPause(); }
        });
		Panel scorePanel = new Panel(new GridLayout(7,1));
		score = new IntLabel("Score: ", 0);
		level = new IntLabel("Level: ", 0);
		lines = new IntLabel("Lines: ", 0);
		tetrisses = new IntLabel("Tetrisses: ", 0);

		nextPanel.add(b, BorderLayout.NORTH);
		nextPanel.add(n, BorderLayout.CENTER);
		nextPanel.add(p, BorderLayout.SOUTH);
        ButtonGroup bs = new ButtonGroup();
        scorePanel.add(radioA = new JRadioButton(new AbstractAction("Music A"){
            public void actionPerformed(ActionEvent e){
                musicB.stop();
                musicA.loop();
            }
        }));
        scorePanel.add(radioB = new JRadioButton(new AbstractAction("Music B"){
            public void actionPerformed(ActionEvent e){
                musicA.stop();
                musicB.loop();
            }
        }));
        scorePanel.add(radioOff = new JRadioButton(new AbstractAction("No music"){
            public void actionPerformed(ActionEvent e){
                musicA.stop();
                musicB.stop();
            }
        }));
        bs.add(radioA);
        bs.add(radioB);
        bs.add(radioOff);
        radioOff.setSelected(true);
        scorePanel.add(score);
        scorePanel.add(level);
        scorePanel.add(lines);
        scorePanel.add(tetrisses);
		panel.add(nextPanel);
		panel.add(scorePanel);
		b.addKeyListener(c);
		p.addKeyListener(c);
        radioA.addKeyListener(c);
        radioB.addKeyListener(c);
        radioOff.addKeyListener(c);
		add(panel, BorderLayout.WEST);
		add(c, BorderLayout.CENTER);
        musicA = this.getAudioClip(this.getCodeBase(), "teta.mid");
        musicB = this.getAudioClip(this.getCodeBase(), "tetb.mid");
        musicC = this.getAudioClip(this.getCodeBase(), "tetc.mid");
	}

	public void update(Observable obs, Object obj)
	{
		if (g.score!=score.getValue()) score.setValue(g.score);
		if (g.level!=level.getValue())
		{
			level.setValue(g.level);
			c.calcDelay();
		}
		if (g.lines!=lines.getValue()) lines.setValue(g.lines);
		if (g.tetrisses!=tetrisses.getValue()) tetrisses.setValue(g.tetrisses);
	}

	public class TetrisCanvas extends Canvas implements KeyListener, Observer, Runnable
	{
		Image backImg, blockImg;
		Graphics backG, blockG;
		Thread anim;
		boolean pause;
		long delay;

		TetrisCanvas()
		{
			try{
				backImg = getImage(getCodeBase(), "back.jpg");
			}
			catch(Exception e){System.out.println(e.toString());}
			MediaTracker tr =  new MediaTracker(this);
			tr.addImage(backImg,0);
			try {tr.waitForAll();} catch (Exception e) {}
			this.addKeyListener(this);
			g.addObserver(this);
			this.setSize(g.width*g.blocksize, g.height*g.blocksize);
			this.setBackground(Color.black);
			calcDelay();
		}

		public void calcDelay()
		{
			delay=(long)Math.max(50,550-44*g.level);
		}

		public void run()
		{
			while(!pause && !g.gameOver)
			{
				g.slideBlock();
				try
				{
					Thread.sleep(delay);
				}
				catch (Exception e){}
			}
		}

        void pressStart() {
            if (g.gameOver) g.reset();
            pause = false;
            if (anim==null)
            {
                anim=new Thread(this);
                anim.start();
            }
        }

		void pressPause()
		{
    		anim=null;
			pause = true;
		}

		public void update(Observable obs, Object obj)
		{
			if (g.gameOver)
			{
				anim=null;
			}
			repaint();
		}

		public void update(Graphics g)
		{ paint(g); }

		public void keyPressed(KeyEvent e)
		{
			if (!pause && !g.gameOver)
			switch (e.getKeyCode())
			{
				case KeyEvent.VK_DOWN:
					if (e.isControlDown()) g.crashBlock();
					else g.slideBlock();
					break;
				case KeyEvent.VK_LEFT:
					if (e.isControlDown()) g.jumpLeftBlock();
					else g.leftBlock();
					break;
				case KeyEvent.VK_RIGHT:
					if (e.isControlDown()) g.jumpRightBlock();
					else g.rightBlock();
					break;
				case KeyEvent.VK_UP: g.turnLeftBlock(); break;
				case KeyEvent.VK_SHIFT: g.turnRightBlock(); break;
				case KeyEvent.VK_ENTER: g.crashBlock(); break;
			}
		}

		public void keyReleased(KeyEvent e)		{}
		public void keyTyped(KeyEvent e)		{}

		public void paint(Graphics gr)
		{
			if (blockImg==null)
			{
				blockImg = createImage(getSize().width, getSize().height);
				blockG = blockImg.getGraphics();
			}
			blockG.drawImage(backImg, 0, 0, this);
			g.paint(blockG);
			gr.drawImage(blockImg, 0, 0, this);
			if (g.gameOver)
			{
				gr.setColor(Color.white);
				gr.drawString("G A M E   O V E R",52,176);
			}
		}

	}
}
import java.awt.*;

public abstract class Block
{
	int currentState;
	State[] state;
    Color color;

	protected Block(Color c)
	{
		state=new State[4];
		currentState=0;
        color=c;
        initStates();
	}

    protected abstract void initStates();

	protected Color getColor() {
        return color;
    }
	
	protected void turnLeft()
	{
		currentState++;
		if (currentState>3) currentState=0;
	}
	protected void turnRight()
	{
		currentState--;
		if (currentState<0) currentState=3;
	}
	
	protected State getCurrentState()
	{
		return state[currentState];
	}
	protected State getLeftState()
	{
		if (currentState==3) return state[0];
		else return state[currentState+1];
	}
	protected State getRightState()
	{
		if (currentState==0) return state[3];
		else return state[currentState-1];
	}
	
	protected Point getPoint(int n)
	{
		return state[currentState].getPoint(n);
	}

    public static Block triangle() {
        return new Block(Color.orange) {
            protected void initStates() {
                state[0]=new State(1,1,2,1,2,2,3,1);
                state[1]=new State(2,0,2,1,2,2,3,1);
                state[2]=new State(1,1,2,1,2,0,3,1);
                state[3]=new State(1,1,2,0,2,1,2,2);
            }
        };
    }

    public static Block square() {
        return new Block(Color.blue) {
            protected void initStates() {
                state[0]=state[1]=state[2]=state[3]=new State(1,1,2,1,1,2,2,2);
            }
        };
    }

    public static Block bar() {
        return new Block(Color.red) {
            protected void initStates() {
                state[0]=state[2]=new State(0,1,1,1,2,1,3,1);
                state[1]=state[3]=new State(2,0,2,1,2,2,2,3);
            }
        };
    }

    public static Block leftHook() {
        return new Block(Color.white) {
            protected void initStates() {
                state[0]=new State(1,1,2,1,3,1,3,2);
                state[1]=new State(2,0,2,1,2,2,3,0);
                state[2]=new State(1,0,1,1,2,1,3,1);
                state[3]=new State(1,2,2,2,2,1,2,0);
            }
        };
    }

    public static Block leftZag() {
        return new Block(Color.cyan) {
            protected void initStates() {
                state[0]=new State(1,1,2,1,2,2,3,2);
                state[1]=new State(2,1,2,2,3,0,3,1);
                state[2]=new State(1,1,2,1,2,2,3,2);
                state[3]=new State(2,1,2,2,3,0,3,1);            }
        };
    }

    public static Block rightHook() {
        return new Block(Color.magenta) {
            protected void initStates() {
                state[0]=new State(1,1,2,1,3,1,1,2);
                state[1]=new State(2,0,2,1,2,2,3,2);
                state[2]=new State(1,1,2,1,3,1,3,0);
                state[3]=new State(1,0,2,0,2,1,2,2);
            }
        };
    }

    public static Block rightZag() {
        return new Block(Color.green) {
            protected void initStates() {
                state[0]=new State(1,2,2,2,2,1,3,1);
                state[1]=new State(2,0,2,1,3,1,3,2);
                state[2]=new State(1,2,2,1,2,2,3,1);
                state[3]=new State(2,0,2,1,3,1,3,2);
            }
        };
    }
}
import javax.swing.JLabel;
import java.awt.Color;

@SuppressWarnings("serial")
public class IntLabel extends JLabel
{
	int value;
	String text;
	
	public IntLabel(String text, int initValue)
	{
		this.text = text;
		this.setForeground(Color.white);
		this.setAlignmentX(JLabel.RIGHT_ALIGNMENT);
		setValue(initValue);
	}
	
	public void setValue(int newValue)
	{
		value = newValue;
		setText(text+value);
	}
	
	public int getValue()
	{
		return value;
	}
}
import java.awt.*;
import java.util.*;

@SuppressWarnings("serial")
public class NextViewer extends Canvas implements Observer
{
	TetrisGrid g;
	Block nextBlock;
	
	NextViewer(TetrisGrid g)
	{
		this.g = g;			
		g.addObserver(this);
		this.nextBlock = g.nextBlock;
		this.setSize(5*g.blocksize,4*g.blocksize);
		this.setBackground(Color.black);
	}
	
	public void update(Observable obs, Object obj)
	{
		if (g.nextBlock!=this.nextBlock)
		{
			this.nextBlock = g.nextBlock;
			repaint();
		}
	}
	public void paint(Graphics gr)
	{
		State s = nextBlock.getCurrentState();
		for (int i=0; i<4; i++)
		{
			TetrisGrid.paintBlock(gr, s.getPoint(i).x, s.getPoint(i).y, g.blocksize, nextBlock.getColor());
		}
	}
}
import java.awt.Point;

public class State
{
	Point[] point;
	
	protected State(Point p0, Point p1, Point p2, Point p3)
	{
		point = new Point[4];
		point[0]=p0;
		point[1]=p1;
		point[2]=p2;
		point[3]=p3;
	}
	
	protected State(int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
	{
		point = new Point[4];
		point[0]=new Point(x0, y0);
		point[1]=new Point(x1, y1);
		point[2]=new Point(x2, y2);
		point[3]=new Point(x3, y3);
	}

	public Point getPoint(int n)
	{
		return point[n];
	}		
}
import java.awt.*;
import java.util.*;

public class TetrisGrid extends Observable
{
	protected int width, height, blocksize;
	protected Block block, nextBlock;
	protected Color[][] c;
	protected int blockX, blockY, score=0, level=0, lines=0, tetrisses=0;
	protected boolean gameOver=false;
	private Random rnd;
	
	public TetrisGrid(int w, int h, int s)
	{
		width = w;
		height = h;
		blocksize = s;
		c = new Color[width][height];
		newBlock();
		setChanged();
	}
	
	public void reset()
	{
		gameOver=false;
		c = new Color[width][height];
		score=level=lines=tetrisses=0;
		nextBlock=null;
		newBlock();
	}

	public void paint(Graphics gr)
	{	
		for (int l=0; l<height; l++)
			for (int i=0; i<width; i++)
				if (c[i][l]!=null)
				{	
					paintBlock(gr, i, l, blocksize, c[i][l]);
				}		
		State s = block.getCurrentState();
		for (int i=0; i<4; i++)
		{
			paintBlock(gr, s.getPoint(i).x + blockX, s.getPoint(i).y + blockY, blocksize, block.getColor());
		}
	}
	
	public static void paintBlock(Graphics gr, int x, int y, int s, Color c)
	{
		gr.setColor(Color.black);
		gr.drawRect(s*x,s*y,s,s);
		gr.setColor(c);
		gr.fillRect(s*x+1,s*y+1,s-1,s-1);
	}	
	
	public Color c(int x,int y)
	{
		return c[x][y];
	}
	
	public void notifyObservers()
	{
		super.notifyObservers();
		setChanged();			
	}
	
	public void slideBlock()
	{
		if (isPossible(block.getCurrentState(), blockX, blockY+1)) blockY++;
		else dropBlock();
		notifyObservers();
	}
	
	public void crashBlock()
	{
		while (isPossible(block.getCurrentState(), blockX, blockY+1)) blockY++;
		dropBlock();
		notifyObservers();
	}
	
	public void leftBlock()
	{
		if (isPossible(block.getCurrentState(), blockX-1, blockY)) 
		{
			blockX--;
			notifyObservers();
		}
	}
	
	public void jumpLeftBlock()
	{
		while (isPossible(block.getCurrentState(), blockX-1, blockY)) blockX--;
		notifyObservers();
	}
	
	public void rightBlock()
	{
		if (isPossible(block.getCurrentState(), blockX+1, blockY)) 
		{
			blockX++;
			notifyObservers();
		}
	}
	
	public void jumpRightBlock()
	{
		while (isPossible(block.getCurrentState(), blockX+1, blockY)) blockX++;
		notifyObservers();		
	}
	
	public void turnRightBlock()
	{
		if (isPossible(block.getRightState(), blockX, blockY)) 
		{
			block.turnRight();
			notifyObservers();
		}
	}
	
	public void turnLeftBlock()
	{
		if (isPossible(block.getLeftState(), blockX, blockY)) 
		{
			block.turnLeft();
			notifyObservers();
		}
	}
	
	public void dropBlock()
	{
		State s=block.getCurrentState();
		for (int i=0; i<4; i++)
			c[s.getPoint(i).x+blockX][s.getPoint(i).y+blockY] = block.getColor();
		int l=0;
		for (int i=0; i<4; i++)
			if (blockY+i < height)
				l+=removeFullLine(blockY+i)?1:0;
		score(l);
		newBlock();
	}
	
	public boolean removeFullLine(int line)
	{
		for (int i=0; i<width; i++)
			if (line<0 || c[i][line]==null) return false;
		for (int l=line; l>=0; l--)
			for (int i=0; i<width; i++)
				if (l==0) c[i][0]=null;
				else c[i][l]=c[i][l-1];
		return true;
	}
	
	public void score(int scoredLines)
	{
		if (scoredLines==4) tetrisses++;		
		int inc=(int)(Math.sqrt(((level+1)*50)));
		for (int i=0; i<scoredLines; i++)
		{
			score+=inc;
			inc+=inc;
		}
		lines+=scoredLines;
		if (lines>10*level+9) level++;
	}
	
	public void newBlock()
	{
		if (nextBlock==null) nextBlock=randomBlock();
		block=nextBlock;
		blockX=3; blockY=-1;
		nextBlock=randomBlock();
		if (!isPossible(block.getCurrentState(), blockX, blockY)) gameOver();
	}
	
	public Block randomBlock()
	{
		if (rnd==null) rnd = new Random();
		int i = (int)(rnd.nextDouble()*7);
		switch (i)
		{		
			case 0: return Block.square();
			case 1: return Block.bar();
			case 2: return Block.rightHook();
			case 3: return Block.leftHook();
			case 4: return Block.leftZag();
			case 5: return Block.rightZag();
			default:return Block.triangle();
		}
	}
	
	public void gameOver()
	{
		gameOver = true;
		for (int l=0; l<height; l++)
			for (int i=0; i<width; i++)
				c[i][l]=new Color((float)Math.random(),(float)Math.random(),(float)Math.random());	
		notifyObservers();
	}
	
	public boolean isPossible(State s, int x, int y)
	{
		try
		{
			for (int i=0; i<4; i++)
				if (c[s.getPoint(i).x+x][s.getPoint(i).y+y]!=null) return false;
		}
		catch (Exception e)
		{
			return false;
		}
		return true;
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.