/** * Board.java * Handle the mouse clicks and subsequent actions for the Boggle program. * This is a program that displays a Boggle board on the screen, comprised * of a 4x4 grid of squares, where each contains a letter. * The user builds words by successively clicking on displayed squares, resulting in the * successive letters being remembered to form a word. There are also on-screen * squares that can be clicked to exit the program or accept a word. The * originally selected letters are selected at random according to their * distribution in the English language. * * Class: CS 107, Spring 2009 * System: BlueJ 2.5, jsdk 1.6, Windows XP * Program #2, Boggle * * @author Dale Reed */ import java.util.Scanner; // allows user input from the keyBoard2 import java.util.Random; // allows getting a random number public class Board { static final int NUMBER_OF_SQUARES = 16; // number of squares, as a constant // Create the random number generator. "Seed" this with the value 1. // Removing the value from the parenthesis will give a different result every // time, which isn't what we want when we are debugging. Random randomNumberGenerator = new Random(1); // declare the 16 squares on the 4 rows of the Board2 Square s0; Square s1; Square s2; Square s3; Square s4; Square s5; Square s6; Square s7; Square s8; Square s9; Square s10; Square s11; Square s12; Square s13; Square s14; Square s15; // declare buttons used to give output to the user, and an "Exit" button Square displayLabel; // displays the text "Word Letters: " on screen Square ResultLabel; // displays the selected letters for a word Square AcceptWordButton; // users presses this to accept the word Square ExitButton; // users presses this to exit program Square ScoreTitleLabel; // Displays the text "Score" on screen Square ScoreLabel; // Displays actual score on screen // variables to store the previous x,y mouse location of a letter clicked. This is used // to ensure only adjacent letters can update the displayed word. Initialize to an off-screen // value so that we can tell when we update these values for the first time. int oldX = -1; int oldY = -1; // store square size static final int SQUARE_SIZE = 60; // Square size static final int OFFSET = 5; // OFFSETs to use in boundaries around playing squares // store score, which is updated as game progresses int score = 0; // Constructor to create the Board2 Board() { // variables used for drawing int x = 65; int y = 30; int startingXValue = 25; String theColor = ""; // first make a large black background square, that will end up being the border Square background = new Square( x-OFFSET, y-OFFSET, (SQUARE_SIZE+OFFSET)*4 + OFFSET, "black", true, ""); // The code below could be shortened by making it 4 calls to a method, where each call sets the values // for that row of squares. It has been left in this expanded form for clarity. // 1st row of 4 squares s0 = new Square( x + (SQUARE_SIZE + OFFSET) * 0, y + (SQUARE_SIZE + OFFSET) * 0, SQUARE_SIZE, "lightGray", true, getLetter() ); s1 = new Square( x + (SQUARE_SIZE + OFFSET) * 1, y + (SQUARE_SIZE + OFFSET) * 0, SQUARE_SIZE, "lightGray", true, getLetter() ); s2 = new Square( x + (SQUARE_SIZE + OFFSET) * 2, y + (SQUARE_SIZE + OFFSET) * 0, SQUARE_SIZE, "lightGray", true, getLetter() ); s3 = new Square( x + (SQUARE_SIZE + OFFSET) * 3, y + (SQUARE_SIZE + OFFSET) * 0, SQUARE_SIZE, "lightGray", true, getLetter() ); // 2nd row s4 = new Square( x + (SQUARE_SIZE + OFFSET) * 0, y + (SQUARE_SIZE + OFFSET) * 1, SQUARE_SIZE, "lightGray", true, getLetter() ); s5 = new Square( x + (SQUARE_SIZE + OFFSET) * 1, y + (SQUARE_SIZE + OFFSET) * 1, SQUARE_SIZE, "lightGray", true, getLetter() ); s6 = new Square( x + (SQUARE_SIZE + OFFSET) * 2, y + (SQUARE_SIZE + OFFSET) * 1, SQUARE_SIZE, "lightGray", true, getLetter() ); s7 = new Square( x + (SQUARE_SIZE + OFFSET) * 3, y + (SQUARE_SIZE + OFFSET) * 1, SQUARE_SIZE, "lightGray", true, getLetter() ); // 3rd row s8 = new Square( x + (SQUARE_SIZE + OFFSET) * 0, y + (SQUARE_SIZE + OFFSET) * 2, SQUARE_SIZE, "lightGray", true, getLetter() ); s9 = new Square( x + (SQUARE_SIZE + OFFSET) * 1, y + (SQUARE_SIZE + OFFSET) * 2, SQUARE_SIZE, "lightGray", true, getLetter() ); s10 = new Square( x + (SQUARE_SIZE + OFFSET) * 2, y + (SQUARE_SIZE + OFFSET) * 2, SQUARE_SIZE, "lightGray", true, getLetter() ); s11 = new Square( x + (SQUARE_SIZE + OFFSET) * 3, y + (SQUARE_SIZE + OFFSET) * 2, SQUARE_SIZE, "lightGray", true, getLetter() ); // 4th row s12 = new Square( x + (SQUARE_SIZE + OFFSET) * 0, y + (SQUARE_SIZE + OFFSET) * 3, SQUARE_SIZE, "lightGray", true, getLetter() ); s13 = new Square( x + (SQUARE_SIZE + OFFSET) * 1, y + (SQUARE_SIZE + OFFSET) * 3, SQUARE_SIZE, "lightGray", true, getLetter() ); s14 = new Square( x + (SQUARE_SIZE + OFFSET) * 2, y + (SQUARE_SIZE + OFFSET) * 3, SQUARE_SIZE, "lightGray", true, getLetter() ); s15 = new Square( x + (SQUARE_SIZE + OFFSET) * 3, y + (SQUARE_SIZE + OFFSET) * 3, SQUARE_SIZE, "lightGray", true, getLetter() ); // initialize the display label, selected letters display button, "Accept" button, and the "Exit" button displayLabel = new Square( x + (SQUARE_SIZE + OFFSET) * 0, y + (SQUARE_SIZE + OFFSET) * 4, SQUARE_SIZE, "white", true, "Word Letters:"); ResultLabel = new Square( x + (SQUARE_SIZE + OFFSET) * 1, y + (SQUARE_SIZE + OFFSET) * 4, SQUARE_SIZE, "white", true, ""); AcceptWordButton =new Square( x + (SQUARE_SIZE + OFFSET) * 3, y + (SQUARE_SIZE + OFFSET) * 4 + OFFSET, SQUARE_SIZE, "gray", true, "Accept"); ScoreTitleLabel = new Square( x + (SQUARE_SIZE + OFFSET) * 0, y + (SQUARE_SIZE + OFFSET) * 5, SQUARE_SIZE, "white", true, "Score:"); ScoreLabel = new Square( x + (SQUARE_SIZE + OFFSET) * 1, y + (SQUARE_SIZE + OFFSET) * 5, SQUARE_SIZE, "white", true, "0"); ExitButton = new Square( x + (SQUARE_SIZE + OFFSET) * 4 + 20, y + (SQUARE_SIZE + OFFSET) * 5 + 20, SQUARE_SIZE, "gray", true, " Exit"); }//end method doIt() //--------------------------------------------------------------------------------- // Handle each mouse click void handleMouseClick(int mouseX, int mouseY) { String idString = ""; // set default for display Square theSquare = null; // will point to which ever Square was clicked, if any // keep track of the middle of the clicked square, to be used in determining square adjacency int selectedSquareMiddleX = -1; // middle X value for selected square int selectedSquareMiddleY = -1; // middle Y value for selected square // get the clicked square. theSquare = getClickedSquare( mouseX, mouseY); // if theSquare has been set (it is not null) so a valid Square was clicked. Store its // middle position to be used in adjacency checking. if( theSquare != null) { // Found the square, so retrieve it's label // trim is used to eliminate any leading or trailing spaces, such as // the spaces on the "Exit" button used to align the text in the button idString = theSquare.getLabel().trim(); } // Now check idString to see what button, if any, was clicked. // Handle "Exit" if that button was clicked if( idString.equals("Exit")) { // exit was chosen System.out.println("Final Score was: " + score); System.out.println("Exiting program..."); System.exit( 0); } else if( idString.equals("Accept")) { // The Accept button was clicked. Display the word and update score String theWord = ResultLabel.getLabel(); System.out.println( theWord); // update score variable based on length of this word, and update displayed score score = score + theWord.length(); ScoreLabel.setLabel("" + score); // clear the word from the screen ResultLabel.setLabel(""); // also now erase the idString so that the text "Accept" doesn't get displayed idString = ""; oldX = oldY = -1; //reset the old square x,y positions used for adjacency checks } else if( !idString.equals("")) { // idString is not empty ("") and was not "Exit" or "Accept" so a board letter // Square must have been clicked. // Set the upper-left corner coordinates for the selected square selectedSquareMiddleX = theSquare.getX() + theSquare.getSize()/2; selectedSquareMiddleY = theSquare.getY() + theSquare.getSize()/2; // Update the answer box by adding the latest letter clicked to what is there so far. // First ensure squares are adjacent by verifying that the distance between the two // clicks in both the x and y directions is less than the size of a square + the square OFFSET // Only do this check when a previous letter has already been clicked (oldX!=-1 and oldY!=-1) // If this is the first letter clicked, store mouse position for adjacency test next turn if( (oldX == -1) && (oldY == -1)) { ResultLabel.setLabel( ResultLabel.getLabel() + idString); // record the x,y position of the selected square for future adjacency // comparison oldX = selectedSquareMiddleX; oldY = selectedSquareMiddleY; } else { // Ensure letter is adjacent to previous letter before updating display string if( (Math.abs( selectedSquareMiddleX - oldX) <= (SQUARE_SIZE + OFFSET)) && (Math.abs( selectedSquareMiddleY - oldY) <= (SQUARE_SIZE + OFFSET)) ) { ResultLabel.setLabel( ResultLabel.getLabel() + idString); oldX = selectedSquareMiddleX; oldY = selectedSquareMiddleY; }//end if }//end else }//end else if( !idString.equals("")) }//end handleMouseClick() //--------------------------------------------------------------------------------- // Return the clicked Square, if there is one // Square getClickedSquare( int mouseX, int mouseY) { Square theSquare = null; // will point to selected Square if there is one // will store retrieved Square x,y and size values int SquareXPosition = -1; int SquareYPosition = -1; int squareSize = -1; // Check each board square to see if it overlaps with mouse click position. // If it does, return that Square. Intersection should only be true for a single // square as long as the squares don't overlap. for( int i=0; i= SquareXPosition) && (mouseX <= (SquareXPosition + squareSize)) && (mouseY >= SquareYPosition) && (mouseY <= (SquareYPosition + squareSize)) ) { return true; } else { return false; } }//end mouseIntersectsSquare() // return the Square that is at the indicated index Square getSquareAtIndex( int i) { Square theSquare = null; // store the reference to the Square matching the index switch( i) { case 0: theSquare = s0; break; case 1: theSquare = s1; break; case 2: theSquare = s2; break; case 3: theSquare = s3; break; case 4: theSquare = s4; break; case 5: theSquare = s5; break; case 6: theSquare = s6; break; case 7: theSquare = s7; break; case 8: theSquare = s8; break; case 9: theSquare = s9; break; case 10: theSquare = s10; break; case 11: theSquare = s11; break; case 12: theSquare = s12; break; case 13: theSquare = s13; break; case 14: theSquare = s14; break; case 15: theSquare = s15; break; }//end switch return theSquare; // return the reference to the Square corresponding to the index }//end getSquareAtIndex() // get a random letter to display on the Board2. Use the following table: /* letter freq cummulative freq. a 8.167 8.167 b 1.492 9.659 c 2.782 12.441 d 4.253 16.694 e 12.702 29.396 f 2.228 31.624 g 2.015 33.639 h 6.094 39.733 i 6.966 46.699 j 0.153 46.852 k 0.772 47.624 l 4.025 51.649 m 2.406 54.055 n 6.749 60.804 o 7.507 68.311 p 1.929 70.24 q 0.095 70.335 r 5.987 76.322 s 6.327 82.649 t 9.056 91.705 u 2.758 94.463 v 0.978 95.441 w 2.36 97.801 x 0.15 97.951 y 1.974 99.925 z 0.074 99.999 */ String getLetter() { String theLetter = ""; // get a random number, which will be used to determine which of 26 letters to choose int theRandomInt = randomNumberGenerator.nextInt(); // Get a possibly large random int // ensure it is a positive value theRandomInt = Math.abs( theRandomInt); // put it in the range 0..100000 theRandomInt = theRandomInt % 100000; // Remember that %100 gives the remainder after dividing by 100 // put it in the range 1..100000 theRandomInt += 1; // add 1 to it float theRandomNumber = theRandomInt / 1000; // put it in the range 0.000 .. 99.999 // use the random number to choose a letter if( theRandomNumber <= 8.167) theLetter = "A"; else if( theRandomNumber <= 9.659) theLetter = "B"; else if( theRandomNumber <= 12.441) theLetter = "C"; else if( theRandomNumber <= 16.694) theLetter = "D"; else if( theRandomNumber <= 29.396) theLetter = "E"; else if( theRandomNumber <= 31.624) theLetter = "F"; else if( theRandomNumber <= 33.639) theLetter = "G"; else if( theRandomNumber <= 39.733) theLetter = "H"; else if( theRandomNumber <= 46.699) theLetter = "I"; else if( theRandomNumber <= 46.852) theLetter = "J"; else if( theRandomNumber <= 47.624) theLetter = "K"; else if( theRandomNumber <= 51.649) theLetter = "L"; else if( theRandomNumber <= 54.055) theLetter = "M"; else if( theRandomNumber <= 60.804) theLetter = "N"; else if( theRandomNumber <= 68.311) theLetter = "O"; else if( theRandomNumber <= 70.24) theLetter = "P"; else if( theRandomNumber <= 70.335) theLetter = "Q"; else if( theRandomNumber <= 76.322) theLetter = "R"; else if( theRandomNumber <= 82.649) theLetter = "S"; else if( theRandomNumber <= 91.705) theLetter = "T"; else if( theRandomNumber <= 94.463) theLetter = "U"; else if( theRandomNumber <= 95.441) theLetter = "V"; else if( theRandomNumber <= 97.801) theLetter = "W"; else if( theRandomNumber <= 97.951) theLetter = "X"; else if( theRandomNumber <= 99.925) theLetter = "Y"; else if( theRandomNumber < 100.000) theLetter = "Z"; return theLetter; }//end getLetter() }// end class Board2()