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