//************************************************************************ // You are free to copy this source and use it in any way you deem fit //************************************************************************ // 3D Mine Sweeper // (I hope I am first to think of this) // // Program: MineSweeper // Programmer: H K Gupta Date: Someday in March 1998 //************************************************************************ import java.awt.*; import java.applet.Applet; import java.lang.Math; import java.util.Date; public class MineSweeper extends Applet implements Runnable{ Panel pLeft; Label[] lHridayesh; Panel pRight; Label lMinesRemaining; Label lTime; Button bNewGame; Label l3D; Label lMine; Label lSweeper; Button bBeginner; Button bIntermediate; Button bExpert; Label lPlayerType; Button bHelp; int playerType = 3; // 2 beginner, 3 intermediate, 4 expert MineSweeperDisplay pMain; boolean firstTime = true; Thread runThread = null; boolean pauseInEffect = false; public void init() { } public void start() { if(firstTime) { setUpDisplay(); firstTime = false; } if (runThread == null) { if(!pauseInEffect) { runThread = new Thread(this, "MineSweeper"); runThread.start(); } } } private boolean imagesLoaded = false; public void run() { if(!imagesLoaded) { try { // Wait for icons file to load pMain.tracker.waitForAll(); } catch (InterruptedException e) {} try { // Wait for mineSweeper image file to load pMain.mineSweeperTracker.waitForAll(); } catch (InterruptedException e) {} imagesLoaded = true; } while (Thread.currentThread() == runThread) { try { Thread.sleep(500); } catch (InterruptedException e){} pMain.timer(); } } public void stop() { runThread = null; } void setUpDisplay() { setLayout(new BorderLayout()); pLeft = new Panel(); pLeft.setLayout(new GridLayout(0, 1)); add("West", pLeft); lHridayesh = new Label[9]; for(int i = 0; i < 9; i++) { lHridayesh[i] = new Label("HRIDAYESH".substring(i, i+ 1), Label.CENTER); lHridayesh[i].setFont(new Font("TimesRoman",Font.BOLD,36)); lHridayesh[i].setForeground(Color.red); pLeft.add(lHridayesh[i]); } pRight = new Panel(); pRight.setLayout(new GridLayout(0, 1)); add("East", pRight); lMinesRemaining = addLabel(pRight, ""); lTime = addLabel(pRight, ""); lTime.setFont(new Font("TimesRoman",Font.BOLD,18)); bNewGame = addButton(pRight, "New Game"); l3D = addLabel(pRight, "3-D"); lMine = addLabel(pRight, "MINE"); lSweeper = addLabel(pRight, "SWEEPER"); bBeginner = addButton(pRight, "Beginner"); bIntermediate = addButton(pRight, "Intermediate"); bExpert = addButton(pRight, "Expert"); lPlayerType = addLabel(pRight, "InterMediate"); bHelp = addButton(pRight, "HELP!!"); pMain = new MineSweeperDisplay(this, getImage(getCodeBase(), "HridayeshGlobeArrows.gif"), getImage(getCodeBase(), "MineSweeper.gif")); add("Center", pMain); validate(); } Button addButton(Panel p, String s) { Button b = new Button(s); p.add(b); return b; } Label addLabel(Panel p, String s) { Label l = new Label(s, Label.CENTER); p.add(l); return l; } public boolean action(Event e, Object arg) { Object target = e.target; if(target == bNewGame) { pMain.startNewGame(); return true; } if(target == bBeginner) { setPlayerType(2); return true; } if(target == bIntermediate) { setPlayerType(3); return true; } if(target == bExpert) { setPlayerType(4); return true; } if(target == bHelp) { processHelp(); return true; } return false; } private void setPlayerType(int type) { playerType = type; if(type == 2) lPlayerType.setText("Beginner"); if(type == 3) lPlayerType.setText("Intermediate"); if(type == 4) lPlayerType.setText("Expert"); pMain.setLevel(type); } void processHelp() { HridayeshHelp window = null; try { window = new HridayeshHelp(); } catch (Exception e) { bHelp.setLabel("Error"); bHelp.disable(); return; } window.setTitle("Mine Sweeper Help"); window.setText( "********* 3D Mine Sweeper ***********" + "\n\n" + "\n We are sure that you do not want help on the rules of the game" + "\n Anybody who uses computers is supposed to know the rules of" + "\n this great game as they are part of your primary education" + "\n\n However here is a brief summary of the the game for those who did" + "\n not pay attention in their classroom" + "\n 1. Use left button to expose clear areas where you think there" + "\n are no mines. (If there is a mine you are doomed)\n" + "\n 2. Use right button to toggle between a mine, a question mark" + "\n and an unexposed position" + "\n\n" + "\n and here we will try to explain some of the extra features" + "\n\n" + "\nInstead of laying out the field in two dimensions as others do, we have gone 3D." + "\nThe mine field is overlaid on a globe which you can rotate using the arrows" + "\nThis will help you in uncovering the hidden parts of the globe" + "\n\n" + "\n We hope that the above help is sufficient" + "\n\n" + "\nIN CASE YOU ARE STILL CONFUSED, WE ARE IN THE PROCESS OF SETTING A 1-900 NUMBER TO" + "\nPROVIDE HAND HOLDING AND A SHOULDER TO CRY ON. ($3.99 per minute charges will apply) " ); window.pack(); window.show(); } } class MineSweeperDisplay extends HridayeshGlobe { private MineSweeper parent; private boolean gameOver = true; private boolean gameOnBoard = false; private boolean mineExploded = false; private long startTime; MediaTracker mineSweeperTracker; private Image mineSweeperImages; public MineSweeperDisplay(MineSweeper parent, Image iconImages, Image mineSweeperImages) { super(3, iconImages); this.parent = parent; this.mineSweeperImages = mineSweeperImages; mineSweeperTracker = new MediaTracker(this); mineSweeperTracker.addImage(this.mineSweeperImages, 0); } public void setLevel(int level) { waitRotate = 100; gameOnBoard = true; setGlobeLevel(level); } public void startNewGame() { setLevel(globeLevel); } private int waitRotate = 0; public void timer() { if(gameOver) { if(waitRotate > 0) { // wait for sometime before autorotate waitRotate--; return; } else { rotate(); return; } } waitRotate = 10; Date dateForTimer = new Date(); int elapsedTime = (int)((dateForTimer.getTime() - startTime) / 1000); parent.lTime.setText(" " + elapsedTime); } private boolean[] isMine; private int[] selectionType; // 0 not selected, 1 open as clear, 2 as mine 3 as Question private int[] neighbourMines; private int[] drawnType; // to optimize and remember the last drawn mine type void globeInit(int maxPoints) { // override the base class method isMine = new boolean[maxPoints]; selectionType = new int[maxPoints]; neighbourMines = new int[maxPoints]; drawnType = new int[maxPoints]; } boolean globeMouseAllowed() { // override the base class method if(gameOver) return false; return true; } void globeJustRotated() { for(int i = 0; i < nPoints; i++) drawnType[i] = -1; } void globeCreate() { // override the base class method fillMines(); Date dateForTimer = new Date(); startTime = dateForTimer.getTime(); if(gameOnBoard) { gameOver = false; waitRotate = 10; } else { // for demo purposes for(int i = 0; i < nPoints; i++) { if(isMine[i]) { selectionType[i] = 2; } else { selectionType[i] = 1; } } } } void globeDrawLine(Graphics o, int i, int j) { if((drawnType[i] == selectionType[i]) && (drawnType[j] == selectionType[j])) return; o.setColor(Color.blue); if((selectionType[i] == 0) || (selectionType[i] == 3) || (selectionType[j] == 0) || (selectionType[j] == 3)) o.setColor(Color.red); o.drawLine(intX[i], intY[i], intX[j], intY[j]); } private int minesRemaining; private int actualMines; private void showMinesRemaining() { parent.lMinesRemaining.setText("Mines Left " + minesRemaining); } private void fillMines() { minesRemaining = nPoints / 5; actualMines = minesRemaining; showMinesRemaining(); for(int i = 0; i < nPoints; i++) { isMine[i] = false; selectionType[i] = 0; neighbourMines[i] = 0; drawnType[i] = -1; } for(int i = 0; i < minesRemaining; i++) { // create minesRemaining mines int j = (int)(Math.random() * (nPoints - i)); // fill j'th free spot int k = 0; while(j >= 0) { if(!isMine[k]) j--; k++; } isMine[k - 1] = true; } // count neighbouring mines for(int i = 0; i < nPoints; i++) { for(int j = 0; j < 6; j++) { if(pntNeighbour[i][j] != -1) { if(isMine[pntNeighbour[i][j]]) neighbourMines[i]++; } } } } void globeInitDisplayPoint(Graphics o, int point) { // override the base class method if(selectionType[point] == drawnType[point]) return; if(drawnType[point] == -2) { o.setColor(Color.white); o.fillRect(intX[point] - 6, intY[point] - 6, 12, 12); drawnType[point] = -1; } } void globeDisplayPoint(Graphics o, int point) { // override the base class method if(selectionType[point] == drawnType[point]) return; for(int j = 0; j < 6; j++) { int k = pntNeighbour[point][j]; if(k >= 0) { if(intZ[k] >= 0) displayPoint(o, k); } } displayPoint(o, point); } private void displayPoint(Graphics o, int point) { if(!imagesCopied) { imagesCopy(); imagesCopied = true; } int imageNumber = 0; switch (selectionType[point]) { case 0: drawnType[point] = 0; return; case 1: if (neighbourMines[point] <= 0) { o.setColor(Color.white); o.fillOval(intX[point] - 3, intY[point] - 3, 6, 6); drawnType[point] = selectionType[point]; return; } imageNumber = neighbourMines[point] - 1; break; case 2: imageNumber = 5; break; case 3: imageNumber = 9; break; case 4: imageNumber = 8; break; case 5: imageNumber = 6; break; case 6: imageNumber = 7; break; } o.drawImage(images[imageNumber], intX[point] - 5, intY[point] - 5, this); drawnType[point] = selectionType[point]; } private Image[] images; private boolean imagesCopied = false; private int noOfImages = 10; private Image[] messageImages; private int noOfMessageImages = 3; private void imagesCopy() { Graphics imageG; images = new Image[noOfImages]; for(int i = 0; i < noOfImages; i++) { images[i] = createImage(10, 10); imageG = images[i].getGraphics(); imageG.drawImage(mineSweeperImages, - ((10 + 2) * i), -1, this); } messageImages = new Image[noOfMessageImages]; for(int i = 0; i < noOfMessageImages; i++) { messageImages[i] = createImage(200, 18); imageG = messageImages[i].getGraphics(); imageG.drawImage(mineSweeperImages, 0, - (12 + (18 + 2) * i), this); } } void globePointSelected(int point, int mask) { // override the base class method if(gameOver || (selectionType[point] == 1)) return; if(mask != 0) { // right button is pressed switch (selectionType[point]) { case 0: selectionType[point] = 2; drawnType[point] = -1; minesRemaining--; if(isMine[point]) actualMines--; break; case 2: selectionType[point] = 3; drawnType[point] = -1; minesRemaining++; if(isMine[point]) actualMines++; break; case 3: selectionType[point] = 0; drawnType[point] = -2; break; default: return; } if((actualMines == 0) && (minesRemaining == 0)) { gameOver = true; mineExploded = false; waitRotate = 0; } showMinesRemaining(); repaint(); return; } if(isMine[point]) { selectionType[point] = 4; openAllMines(); mineExploded = true; gameOver = true; } else { if(selectionType[point] != 0) return; if(neighbourMines[point] == 0) { selectionType[point] = 1; while(openClear()) {}; // open all clear mines in neighbourhood } else { selectionType[point] = 1; } } repaint(); } private boolean openClear() { // open the neighbour of an opened clear area for (int i = 0; i < nPoints; i++) { if((!isMine[i]) && (selectionType[i] != 2)) { if(selectionType[i] != 1) { for(int k = 0; k < 6; k++) { if(pntNeighbour[i][k] != -1) { if(neighbourMines[pntNeighbour[i][k]] == 0) { if(selectionType[pntNeighbour[i][k]] == 1) { selectionType[i] = 1; return true; } } } } } } } return false; } private void openAllMines() { for(int i = 0; i < nPoints; i++) { if(isMine[i]) { if(selectionType[i] != 4) { // not the error point if(selectionType[i] != 2) selectionType[i] = 6; } } else { if(selectionType[i] == 2) { selectionType[i] = 5; // was not a mine } } } } private Color[] rainbow = { Color.red, Color.yellow, Color.black, Color.green, Color.blue, Color.orange, Color.darkGray, Color.magenta}; private int[] rainbowX = { 150, 106, 0, -106, -150, -106, 0, 106}; private int[] rainbowY = { 0, 106, 150, 106, 0, -106, -150, -106}; private int rainbowIndex = 0; void globeFinalUpdate(Graphics o, int xCenter, int yCenter) { // override the base class method if(waitRotate > 0) return; int messageNumber; if(!gameOnBoard) { messageNumber = 0; } else { if(mineExploded) { messageNumber = 2; } else { messageNumber = 1; } } o.drawImage(messageImages[messageNumber], xCenter - 100, yCenter - 9, this); rainbowIndex++; if(rainbowIndex >= 8) rainbowIndex -= 8; for(int i = 0; i < 8; i++) { int c = i + rainbowIndex; if(c >= 8) c -= 8; o.setColor(rainbow[c]); o.fillOval(xCenter + rainbowX[i] - 10, yCenter + rainbowY[i] - 10, 20, 20); } } }