///////////////////////////////////////////////////////////////////////////////////////////// // Towers (applet) ////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// // Written by: Edward Craft // CMSC 498A -- Indep. Studies Proj. // Last Updated: 11/16/96 ///////////////////////////////////////////////////////////////////////////////////////////// // // Divide applet region into three sections of equal width--these are to be assiciated with // towers 1, 2, and 3, respectively // // init: instantiate 3 HTowers and initialize tower region variables (for bounds // checking) // // repaint: draw all 3 HTowers and display current number of moves; display a message when // all disks have been moved from the tower #1 to tower #3 // // General operation: 1) Initialize all towers and print screen // 2) Whenever mouse button is pressed, "pick up" disk from // top of appropriate tower, and have that disk's image follow // the mouse until the button is released // 3) Once the button is released, drop the disk--if it is in the region // of a valid tower, place it on top; else, send it back home // // Maintain "moves" counter -- only count "valid" moves // ///////////////////////////////////////////////////////////////////////////////////////////// // Future Updates ////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// // // 1) Add double-buiffering to smooth animation // 2) Add graphical disks // 3) Add "gong" sound (take name of sound as parameter; if there is non, play nothing) // 4) Update web page to include the "story" of the towers and instructions // ///////////////////////////////////////////////////////////////////////////////////////////// import java.applet.*; import java.awt.*; import java.net.URL; import java.net.MalformedURLException; public class Towers2 extends Applet { // Variables for "mechanics" of applet--user interface information, etc. // --------------------------------------------------------------------- private int applet_width = 600; // Size of the applet window private int applet_height = 300; private Image OffScreenImage; // Off-screen image for double-buffering private Graphics OffScreenGraphics; // Interface to our "double-buffer" private int mouse_x, mouse_y; // Position of the mouse private int one_third; // Divide screen into thirds private int x1, x2, x3, y; // Variables which describe state of the puzzle // -------------------------------------------- private boolean Holding_Disk; // Are we "holding" a disk with the mouse? private int Disk_Being_Held; // What is the weight of the disk we are // holding? private int From; // Moving disk FROM tower # private int To; // Moving disk TO tower # private boolean Finished; // Has the user completed the game? // (i.e. moved all didsks from tower 1 // to tower 3) private int Num_Disks; // Take number of disks as a parameter private String Num_Disks_String; private HTower tower[]; // 3 towers, each with ? number of disks private Image baseImage; // Image for tower's base (optional) private Image diskImages[]; // Array of disk images (optional) private boolean useImages; // Are we using the image array? private int moveCounter = 0; // How many moves have been made so far? private int optimalMoves; // Optimal number of moves required // (2^Num_Disks)-1; ///////////////////////////////////////////////////////////////////////////////////////////// // Methods ///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////// public void init() { resize(applet_width, applet_height); // Set size of applet window // Initialize our double-buffering vars OffScreenImage = createImage(applet_width, applet_height); OffScreenGraphics = OffScreenImage.getGraphics(); mouse_x = 125; // Place mouse at arbitrary point on screen mouse_y = 125; one_third = ((applet_width)/3); // Figure out where the three towers should // be positioned along the x-axis x1 = (int)(.5*(one_third)); x2 = (int)(1.5*(one_third)); x3 = (int)(2.5*(one_third)); y = applet_height-25; // Place all towers at the same y-coordinate // Some code to get number of disks as a // parameter string, try to convert it to // an int, and handle any errors that might // result by defaulting to "1" disk Num_Disks_String = getParameter("numDisks"); try { Num_Disks = Integer.parseInt(Num_Disks_String); } catch (NumberFormatException e) { Num_Disks = 1; } diskImages = new Image[Num_Disks]; // Num_Disks disk images // (Base of tower handled // separately) String baseName; String diskName; URL baseURL; URL diskURL; URL me = getCodeBase(); useImages = true; for (int i=0; (i < Num_Disks && useImages); i++) // Try to load Num_Disks images { // plus a base image diskName = getParameter("diskImageFile"+(i+1)); try { if (i==0) // First time through this loop, { // also try to load base image baseName = getParameter("baseImageFile"); baseURL = new URL(me, baseName); baseImage = getImage(baseURL); System.out.println("Loading Base Image "+baseURL); } diskURL = new URL(me, diskName); diskImages[i] = getImage(diskURL); System.out.println("Loading Image "+diskURL); } catch (MalformedURLException e) // If unsuccessful, set flag to { // "don't use images" useImages = false; System.out.println("Failed To Load Image "); } } //End "for" tower = new HTower[3]; // Instantiate and empty HTower array // and Call appropriate HTower if (!useImages) // constructors { tower[0] = new HTower(x1, y, Num_Disks, Num_Disks); tower[1] = new HTower(x2, y, Num_Disks, 0); tower[2] = new HTower(x3, y, Num_Disks, 0); } else { // And call the constructors tower[0] = new HTower(x1, y, Num_Disks, Num_Disks, baseImage, diskImages); tower[1] = new HTower(x2, y, Num_Disks, 0, baseImage, diskImages); tower[2] = new HTower(x3, y, Num_Disks, 0, baseImage, diskImages); } optimalMoves = (int)Math.pow(2,Num_Disks)-1; // Calculate optimal number of moves From = 0; // Not holding any disks right now To = 0; Holding_Disk = false; Disk_Being_Held = 0; Finished = false; // We are just beginning! repaint(); } // End init //------------------------------------------------------------------------------------------- /** * Handles double-buffering. */ public synchronized void update(Graphics g) { OffScreenGraphics.setColor(Color.lightGray); // Clear our "canvas" OffScreenGraphics.fillRect(0,0,applet_width,applet_height); paint(OffScreenGraphics); // Do applet painting to our "canvas" g.drawImage(OffScreenImage, 0, 0, this); // Paint the real graphics window } // End update //------------------------------------------------------------------------------------------- public void paint(Graphics g) { g.setColor(Color.black); // Draw a box around our applet g.drawRect(0,0,(applet_width-1), (applet_height-1)); if ( Finished ) // If we have finished the puzzle { // Print message and # moves information g.drawString("Congratulations! You did it!",15,15); g.drawString(" You made "+moveCounter+" moves.",15,25); // Compare moveCounter to optimal soln. if (moveCounter == optimalMoves) g.drawString(" That is the optimal number of moves!",15,35); else g.drawString(" The optimal solution requires only "+optimalMoves+" moves.",15,35); } else // else display # of moves made so far g.drawString("Number of Moves = "+moveCounter, 15, 15); for (int i=0; i<3; i++) // Display our towers tower[i].paint(g); // If we are currently "holding" a disk, // "drag" it along with the mouse if ( Holding_Disk ) { System.out.println("Dragging disk of weight "+Disk_Being_Held); System.out.println(" Image Index = "+(Disk_Being_Held-1)); System.out.println(" Length of Image Array = "+diskImages.length); if (!useImages) g.drawString(""+Disk_Being_Held, mouse_x, mouse_y); else { int imageIndex = Disk_Being_Held - 1; int x_pos = mouse_x - (diskImages[imageIndex].getWidth(this)/2); int y_pos = mouse_y - (diskImages[imageIndex].getHeight(this)/2); g.drawImage(diskImages[imageIndex], x_pos, y_pos, this); } } } // End paint //------------------------------------------------------------------------------------------- public boolean mouseMove(Event evt, int x, int y) { mouse_x = x; // Record new position of the mouse mouse_y = y; return true; } // End mouseMove //------------------------------------------------------------------------------------------- /** * Drag disk across screen, along with the mouse cursor. */ public boolean mouseDrag(Event evt, int x, int y) { mouse_x = x; // Record position of the mouse mouse_y = y; repaint(); // And redraw the applet--if we are currently // holding a disk, it will be drawn at the // mouse's position (this allows us to "drag" // them from one tower to the next return true; } // End mouseDrag //------------------------------------------------------------------------------------------- /** * "Pick up" disk -- Figure out which tower region we are in. If tower not empty, * 1) Take top disk off of tower, * 2) Set "From" pointer to that tower, * 3) Set "Holding_Disk" flag to true, * 4) Repaint the screen. */ public boolean mouseDown(Event evt, int x, int y) { From = -1; for (int i=0; i<3; i++) { if ( tower[i].inXRange(x,(int)(.5*one_third)) ) From = i; } if ( From != -1 ) { Disk_Being_Held = tower[From].pop(); Holding_Disk = ( Disk_Being_Held > 0 ); } repaint(); return true; } // End MouseDown //------------------------------------------------------------------------------------------- /** * "Drop" disk -- Determine which tower's region we are in and whether or not move is valid, * and then place the disk. */ // * If move is valid, drop disk on destination tower; otherwise, return it to its // tower of origin. // * Set "HOLDING DISK" to false // * Increment "moveCounter" // * Check to see if that was the last disk (i.e. all MmaxDisks now reside on toewr #3) // and set the "Finished" flag accordingly // * Repaint the screen public boolean mouseUp(Event evt, int x, int y) { To = -1; for (int i=0; i<3; i++) { if ( tower[i].inXRange(x,(int)(.5*one_third)) ) To = i; } if ( (To != -1) && (Disk_Being_Held != -1) ) { if ( tower[To].push(Disk_Being_Held) ) { if (To != From) // Don't count move if disk is returned { // to its original position moveCounter++; if ( tower[2].NumberOfDisks() == Num_Disks ) // If tower 3 now contains all of Finished = true; // the disk, we are finished } } else tower[From].push(Disk_Being_Held); } Holding_Disk = false; repaint(); return true; } // End MouseUp } // End Towers