//*********************** Copy right information ************************* // FAQ's // Q. Is this program copy right protected? // Ans. No. Even if it was you will copy it anyway so what is the point // Q. Can I use it for commercial purposes? // Ans. Not This program. Somebody may sue you (Not us.We do not do such // things.) However you can use other programs at this site for any // purpose you deem fit. // Q. How do You feel about my copying this software? // Ans. We think that you are doing us a great honour by copying this // software. We are not worthy of this honour but we will appreciate // your generousity if you will simply e-mail us a thank you note // Q. What do I do if I find a bug? // Ans. Go to KMart. Buy a bug repellent(exterminator preferred) and kill // the beast. We dont like bugs. They are Yuk!!! // Q. Where is the code documentation? // Ans. We ourselves are wondering about that. Last time we saw it the code // documentation was headed towards outer space. We have a distinct feeling // that it was trying to run away from us. // Q. How was the code tested and to what degree? // Ans. It was not. But we are sure that somebody will test it, fix up all // the bugs and send us the modified code. Just like US drugs problem // which every body expects to be fixed by itself. //************************************************************************ // Program: Funtris (A variation on famous game program Tetris.) // This goes on to show just how pathetic we are in innovation // department. // Programmer: H K Gupta Date: 10 July 1997 // modified : 16 Jan 1999 //************************************************************************ import java.awt.*; import java.applet.Applet; import java.lang.Math; import java.lang.String; public class Funtris extends Applet implements Runnable{ Panel pGame; // Game area FuntrisDisplay tGame; Panel pStatus; FuntrisBlockLine blkLine; // to display incoming future blocks Button bCheat; boolean isCheat = false; // If yes display incoming blocks Panel pLevel; Button[] bSetLevel; // Set level 1 to 9 Label lScore; // Display level and score int score = 0; int level = 1; int nRowsCompleted = 0; Button bNewGame; Button bPause; boolean pauseInEffect = false; Panel pCrazy; Button bNormal; // Standard Tetris objects Label lGameType; Button bExtended; // extended object range but with 4 cells Button bGoCrazy; // no limit on no. of cells Button bUpsideDown; Button bHelp; Panel pLeft; Label[] lFuntris; int[][][] fixObjects = { // regular objects {{0, 0, 0, 0}, {1, 1, 1, 1}}, {{0, 2, 2, 0}, {0, 2, 2, 0}}, {{3, 3, 3, 0}, {3, 0, 0, 0}}, {{0, 4, 4, 4}, {0, 0, 0, 4}}, {{0, 0, 6, 6}, {0, 6, 6, 0}}, {{5, 5, 0, 0}, {0, 5, 5, 0}}, {{0, 7, 7, 7}, {0, 0, 7, 0}}, // Extended objects but with 4 cells {{1, 1, 0, 0}, {0, 0, 2, 2}}, {{3, 0, 3, 0}, {0, 4, 0, 4}}, {{1, 1, 1, 0}, {0, 0, 0, 2}}, {{0, 3, 3, 3}, {4, 0, 0, 0}}, {{0, 2, 2, 0}, {3, 0, 0, 3}}, // Crazy objects with any no. of cells {{2, 2, 2, 2}, {2, 2, 2, 2}}, {{0, 4, 0, 0}, {0, 0, 4, 0}}, {{3, 0, 0, 3}, {3, 0, 0, 3}}, {{2, 2, 0, 4}, {2, 0, 4, 4}}, {{1, 1, 1, 0}, {0, 4, 4, 4}} }; int numStandard = 7; int numExtended = 12; int numCrazy = 17; int nObjects = numStandard; // Standard game value. But we can be different Color[] cTable = {Color.black, Color.red, Color.green, Color.yellow, Color.blue, Color.gray, Color.cyan, Color.magenta}; int cellWidth; int nRows = 20; int nColumns = 10; int[][] cellGrid; // Entire Funtris grid except the moving object int currentObject = 0; // for debugging only int nextObject = 0; int rCurrent, cCurrent; // object coordinates int[][] objBuffer; // 4 * 4 buffer to hold current object int[][] tmpBuffer; // for object rotation boolean upsideDown = false; boolean firstTime = true; boolean gameOver = false; Thread runThread = null; int motionType = 0; // 1 Left 2 Right 3 Left Rotate 4 right rotate 5 drop public void start() { if (firstTime) { setUpDisplay() ; firstTime = false; } if (runThread == null) { if(!pauseInEffect) { runThread = new Thread(this, "Funtris"); runThread.start(); } } } private int cycleCount = 0; private int sleepTime; private int sleepTic = 17; public void run() { while (Thread.currentThread() == runThread) { cycleCount++; if(cycleCount > 2) cycleCount = 0; sleepTime = Math.max(sleepTic * 5 - level * sleepTic, 17); if(cycleCount == 0) { move(); try { Thread.sleep(sleepTime * 3); } catch (InterruptedException e){ } } else { implementMotion(); try { Thread.sleep(sleepTime); } catch (InterruptedException e){ } } } } public void stop() { runThread = null; } void setUpDisplay() { setLayout(new BorderLayout()); pGame = new Panel(); pGame.setLayout(new BorderLayout()); add("Center", pGame); cellGrid = new int[nRows][nColumns]; tGame = new FuntrisDisplay(this, nRows, nColumns); objBuffer = new int[4][4]; tmpBuffer = new int[4][4]; pGame.add("Center", tGame); pStatus = new Panel(); pStatus.setLayout(new GridLayout(0, 1)); add("East", pStatus); blkLine = new FuntrisBlockLine(this); pStatus.add(blkLine); bCheat = new Button("Cheat"); pStatus.add(bCheat); pLevel = new Panel(); pLevel.setLayout(new GridLayout(0, 9)); pStatus.add(pLevel); bSetLevel = new Button[9]; for(int i = 0; i < 9; i++) { bSetLevel[i] = new Button("" + (i + 1)); pLevel.add(bSetLevel[i]); } lScore = new Label(" ", Label.CENTER); lScore.setFont(new Font("TimesRoman",Font.BOLD,12)); pStatus.add(lScore); updateScore(0); bNewGame = new Button("Start New Game"); pStatus.add(bNewGame); bPause = new Button("Pause"); pStatus.add(bPause); pCrazy = new Panel(); pCrazy.setLayout(new GridLayout(0, 2)); pStatus.add(pCrazy); bNormal = new Button("Standard"); pCrazy.add(bNormal); lGameType = new Label("Standard"); pCrazy.add(lGameType); bExtended = new Button("Extended"); pCrazy.add(bExtended); bGoCrazy = new Button("Go Crazy"); pCrazy.add(bGoCrazy); bUpsideDown = new Button("Upside Down"); pStatus.add(bUpsideDown); bHelp = new Button("Help me god!!!"); pStatus.add(bHelp); pLeft = new Panel(); pLeft.setLayout(new GridLayout(0, 1)); add("West", pLeft); lFuntris = new Label[7]; for(int i = 0; i < 7; i++) { lFuntris[i] = new Label("* " + "FUNTRIS".substring(i, i+ 1) + " *", Label.CENTER); lFuntris[i].setFont(new Font("TimesRoman",Font.BOLD,36)); lFuntris[i].setForeground(Color.red); pLeft.add(lFuntris[i]); } keyBuffer = new int[keyBufferSize]; startNewGame(1); validate(); } public boolean action(Event e, Object arg) { Object target = e.target; if(target == bCheat) { if(isCheat) { isCheat = false; bCheat.setLabel("Cheat"); } else { isCheat = true; bCheat.setLabel("No More Cheating"); } blkLine.drawNextObject(); return true; } if(target == bPause) { if(pauseInEffect) { pauseInEffect = false; bPause.setLabel("Pause"); start(); } else { pauseInEffect = true; bPause.setLabel("Resume"); stop(); } return true; } if(target == bNewGame) { bNewGame.setLabel("New Game"); startNewGame(1); return true; } for(int i = 0; i < 9; i++) { if(target == bSetLevel[i]) { bNewGame.setLabel("New Game"); startNewGame(i + 1); } } if(target == bNormal) { nObjects = numStandard; lGameType.setText("Standard"); return true; } if(target == bExtended) { nObjects = numExtended; lGameType.setText("Extended"); return true; } if(target == bGoCrazy) { nObjects = numCrazy; lGameType.setText("Gone Crazy"); return true; } if(target == bUpsideDown) { if(!upsideDown) { bUpsideDown.setLabel("I got a Headache"); } else { bUpsideDown.setLabel("Upside Down"); } upsideDown = !upsideDown; motionType = 6; return true; } if(target == bHelp) { processHelp(); return true; } return false; } void implementMotion() { if (motionType == 6) { tGame.redrawGrid(); motionType = 0; return; } if(gameOver) { // dump the user moves in black hole motionType = 0; keysInBuffer = 0; return; } tryOneActualMotion(); motionType = 0; } void tryOneActualMotion() { while(keysInBuffer > 0) { getNextMotion(); if(tryMove()) { tGame.drawObject(); if(motionType == 5) mergeObjectWithGrid(); return; } else { tGame.putObject(); } } } boolean tryMove() { tGame.removeObject(); switch (motionType) { case 1: return(moveObjectCandR(-1, 0)); // Left Move case 2: return(moveObjectCandR(1, 0)); // Right Move case 3: // Left Rotate rotateObjectLeft(); if(moveObjectCandR(0, 0)) return true; rotateObjectRight(); return false; case 4: // Right rotate rotateObjectRight(); if(moveObjectCandR(0, 0)) return true; rotateObjectLeft(); return false; case 5: // Drop the object to bottom while(moveObjectCandR(0, 1)) {}; // keep dropping object till it stops return true; default:break; } return false; } public boolean keyDown(Event e, int key) { if((key == Event.LEFT) || (key == 'j')) return(registerMotionType(1)); if((key == Event.RIGHT) || (key == 'l')) return(registerMotionType(2)); if(!upsideDown) { if((key == Event.UP) || (key == 'i')) return(registerMotionType(3)); if((key == Event.DOWN) || (key == 'k')) return(registerMotionType(5)); } else { if((key == Event.UP) || (key == 'i')) return(registerMotionType(5)); if((key == Event.DOWN) || (key == 'k')) return(registerMotionType(4)); } return false; } private int keyBufferSize = 10; private int[] keyBuffer; private int keysInBuffer = 0; boolean registerMotionType(int type) { if(gameOver) return true; if(-1 == currentObject) return true; if(keysInBuffer < keyBufferSize) { keyBuffer[keysInBuffer] = type; keysInBuffer++; } return true; } void getNextMotion() { if(keysInBuffer < 1) { motionType = 0; return; } motionType = keyBuffer[0]; keysInBuffer--; if(keysInBuffer > 0) { // move buffer up for(int i = 0; i < keysInBuffer; i++) { keyBuffer[i] = keyBuffer[i + 1]; } } } void updateScore(int scr) { score += scr; lScore.setText("Level " + level + " Score " + score); } void move() { if(gameOver) return; int i, j; if(currentObject == -1) { // no object in main area currentObject = nextObject; nextObject = (int)(Math.random() * nObjects); blkLine.drawNextObject(); for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) objBuffer[i][j] = 0; for(i = 1; i < 3; i++) for(j = 0; j < 4; j++) objBuffer[i][j] = fixObjects[currentObject][i - 1][j]; cCurrent = (nColumns / 2) - 2; rCurrent = -4; // Object is on top of main area } tGame.removeObject(); if(moveObjectCandR(0, 1)) { tGame.drawObject(); } else { // Object can not go down further tGame.putObject(); mergeObjectWithGrid(); } } boolean moveObjectCandR(int c, int r) { if(currentObject == -1) return false; // very serious timing bug was detected PHEW! int i, j; for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { if(0 != objBuffer[i][j]) { if(((rCurrent + i + r) >= nRows) || ((cCurrent + j + c) >= nColumns) || ((cCurrent + j + c) < 0)) { return false; // Sorry folks no luck } if((rCurrent + i + r) >= 0) { if(cellGrid[rCurrent + i + r][cCurrent + j + c] > 0) return false; // sorry again spot occupied } } } } // What luck. Object can actually be moved. We are GREAT or what? rCurrent += r; cCurrent += c; return true; // Eureka. I did it } void mergeObjectWithGrid() { // we have landed int i, j, k; for(i = 0; i < 4; i++) { for(j = 0; j < 4; j++) { if(0 != objBuffer[i][j]) { if((rCurrent + i) >= 0) cellGrid[rCurrent + i][cCurrent + j] = objBuffer[i][j]; } } } currentObject = -1; // object is now invalid boolean flag = false; for(i = 0; i < nRows; i++) { // check for completed rows if(rowCompleted(i)) { removeRow(i); updateScore(level * 10); nRowsCompleted++; if(nRowsCompleted >= 10) { level++; updateScore(0); // show changed level nRowsCompleted = 0; } flag = true; } } if(flag) { // Delay and compress the cellGrid tGame.redrawGrid(); try { Thread.sleep(500); } catch (InterruptedException e){ } for(i = nRows - 1; i > 0; i--) { if(blankRow(i)) { j = nonBlankRow(i - 1); if(j > -1) { // copy and clear for(k = 0; k < nColumns; k++) { cellGrid[i][k] = cellGrid[j][k]; cellGrid[j][k] = 0; } } } } tGame.redrawGrid(); // Display is sufficiently damaged } // check to see end of game if first row is non blank for(i = 0; i < nColumns; i++) { if(0 != cellGrid[0][i]) { gameOver = true; bNewGame.setLabel("Game Over. New Game"); } } } void removeRow(int i) { // remove row from display as well as grid int j; for(j = 0; j < nColumns; j++) cellGrid[i][j] = 0; // Add dramatics later tGame.drawRow(i); // simple living and high thinking } int nonBlankRow(int strt) { // find a non blank row int i, j; for(i = strt; i > -1; i--) for(j = 0; j < nColumns; j++) if(0 != cellGrid[i][j]) return i; return -1; // No nonblank row left } boolean blankRow(int i) { // check if all grid cells are empty int j; for(j = 0; j < nColumns; j++) { if(0 != cellGrid[i][j]) return false; } return true; } boolean rowCompleted(int i) { // check if all grid cells are full int j; for(j = 0; j < nColumns; j++) { if(cellGrid[i][j] == 0) return false; } return true; } void startNewGame(int lvl) { initCellGrid(); score = 0; level = lvl; nRowsCompleted = 0; motionType = 0; currentObject = -1; // no more valid nextObject = (int)(Math.random() * nObjects); tGame.laidOut = false; gameOver = false; keysInBuffer = 0; updateScore(0); } void initCellGrid() { int i, j; for(i = 0; i < nRows; i++) { for(j = 0; j < nColumns; j++) { cellGrid[i][j] = 0; // set blank if(i > (nRows - 4)) { // Fill bottom three rows randomly cellGrid[i][j] = (int)(Math.random() * 5); } } } } void rotateObjectLeft() { int i, j; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) tmpBuffer[i][j] = objBuffer[i][j]; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) objBuffer[i][j] = tmpBuffer[j][3 -i]; } void rotateObjectRight() { int i, j; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) tmpBuffer[i][j] = objBuffer[i][j]; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) objBuffer[i][j] = tmpBuffer[3 - j][i]; } void processHelp() { HridayeshHelp window = null; try { window = new HridayeshHelp(); } catch (Exception e) { bHelp.setLabel("Error"); bHelp.disable(); return; } window.setTitle("Funtris Help"); window.setText( "********* This help is a direct transmission from GOD ***********" + "\n\n" + "\nWe are sure that you do not want help on the rules of the game" + "\nAnybody who uses computers is supposed to know the rules of" + "\nthis great game as they are part of your primary education" + "\n" + "\nHowever we will explain some of the extra features" + "\n" + "\nExtended: This lets you add few extra type of items but with four blocks" + "\n" + "\nGo Crazy: This leads to mayhem as all kinds of funny object appear in the play" + "\n" + "\nUpside Down: For those of you who play this game while doing head stand" + "\n" + "\nIF YOU ARE ANY WISER AFTER READING THIS HELP MESSAGE" + "\nWE HAVE FAILED IN OUR DUTY TO PROVIDE A TOTALLY USELESS HELP " ); window.pack(); window.show(); } } class FuntrisBlockLine extends Canvas { // For those who dare to cheat private Funtris parent; public FuntrisBlockLine(Funtris parent) { super(); this.parent = parent; setBackground(Color.black); } public void drawNextObject() { // Setup object type and repaint repaint(); } public void paint(Graphics g) { if(!parent.isCheat) return; // No need to draw as the user does not cheat int xOff, yOff; Dimension d = size(); int w = parent.cellWidth; xOff = (d.width - (w * 4)) / 2; yOff = (d.height - (w * 2)) / 2; int i, j; for(i = 0; i < 2; i++) { for(j = 0; j < 4; j++) { g.setColor(parent.cTable[parent.fixObjects[parent.nextObject][i][j]]); g.fillRect( xOff + j * w, yOff + i * w, w, w); if(0 != parent.fixObjects[parent.nextObject][i][j]) { g.setColor(Color.white); g.drawRect( xOff + j * w, yOff + i * w, w - 1, w - 1); } } } } } class FuntrisDisplay extends Canvas { private int rows, columns; private int cellWidth; private int xOff, yOff; boolean laidOut = false; private Funtris parent; private int paintType = -4; // -1 remove object, -2 draw object, -3 redraw grid, +ve draw row private Image offImage; private Graphics o; private boolean bufferAllocated = false; public FuntrisDisplay(Funtris parent, int rows, int columns) { super(); this.parent = parent; this.rows = rows; this.columns = columns; } private void calculateSizes() { Dimension d = size(); if(!bufferAllocated) { offImage = createImage(d.width, d.height); o = offImage.getGraphics(); Color bg = getBackground(); o.setColor(bg); o.fillRect(0, 0, d.width, d.height); bufferAllocated = true; } if((d.height / rows) < (d.width / columns)) { cellWidth = d.height / rows; } else { cellWidth = d.width / columns; } parent.cellWidth = cellWidth; xOff = (d.width - (cellWidth * columns)) / 2; yOff = (d.height - (cellWidth * rows)) / 2; } public void paint(Graphics g) { update(g); } public void update(Graphics g) { int i; if(paintType == -4) laidOut = false; // Request from window change if (!laidOut) { laidOut = true; calculateSizes(); o.setColor(Color.black); o.fillRect(xOff, yOff, cellWidth * columns, cellWidth * rows); for(i = 0; i < rows; i++) drawSingleRow(i); } if(paintType != -1) g.drawImage(offImage, 0, 0, this); paintType = -4; } void removeObject() { int i, j; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) if(0 != parent.objBuffer[i][j]) drawSingleCell(parent.rCurrent + i, parent.cCurrent + j, 0); } void putObject() { int i, j; for(i = 0; i < 4; i++) for(j = 0; j < 4; j++) if(0 != parent.objBuffer[i][j]) drawSingleCell(parent.rCurrent + i, parent.cCurrent + j, parent.objBuffer[i][j]); } void drawObject() { putObject(); if(parent.currentObject != -1) issueRepaint(-2); } void drawRow(int i) { drawSingleRow(i); } void redrawGrid() { laidOut = false; issueRepaint(-3); } private void drawSingleRow(int currRow) { int j; int w = cellWidth; for(j = 0 ; j < columns; j++) { drawSingleCell(currRow, j, parent.cellGrid[currRow][j]); } } private void drawSingleCell(int i, int j, int clr) { if( i < 0) return; // Object cell may be on top of game area int w = cellWidth; o.setColor(parent.cTable[clr]); if(parent.upsideDown) i = rows - 1 - i; if(0 != clr) { o.fillRect( xOff + j * w + 1, yOff + i * w + 1, w - 2, w - 2); o.setColor(Color.white); o.drawRect( xOff + j * w, yOff + i * w, w - 1, w - 1); } else { o.fillRect( xOff + j * w, yOff + i * w, w, w); } } private void issueRepaint(int type) { delay(); paintType = type; repaint(); delay(); } private void delay() { while(-4 != paintType) { // Wait for paint to be over try { Thread.sleep(10); } catch (InterruptedException e){} } } }