Solution for
Programming Exercise 6.1


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 6.1: Write an applet that shows a pair of dice. When the user clicks on the applet, the dice should be rolled (that is, the dice should be assigned newly computed random values). Each die should be drawn as a square showing from 1 to 6 dots. Since you have to draw two dice, its a good idea to write a subroutine, "void drawDie(Graphics g, int val, int x, int y)", to draw a die at the specified (x,y) coordinates. The second parameter, val, specifes the value that is showing on the die. Assume that the size of the applet is 100 by 100 pixels. Here is a working version of the applet. (My applet plays a clicking sound when the dice are rolled. See the solution to see how this is done.)


Discussion

The applet must be declared to "implement MouseListener", and it must define the mousePressed() method to roll the dice. (The other methods of the MouseListener interface will be empty.) The applet's init() method must call addMouseListener(this) to register the applet to listen for mouse events on itself. I defined the mousePressed() routine simply as

        public void mousePressed(MouseEvent evt) {
           roll();
        }

and I defined the roll() subroutine to roll the dice. It would be reasonable to put the code for rolling the dice in the mousePressed() routine, but writing a subroutine to do it makes the program a little easier to modify for the next two exercises. Anyway, when you can identify a self-contained, meaningful task to be performed, it's never a bad a idea to write a subroutine to do it. It will make the program more readable, if nothing else. My roll subroutine assigns random values to the dice and calls repaint() so that the new values will be shown. It also plays the clicking sound with the command

                    play(getCodeBase(), "click.au");

The play() method is a predefined instance method in the Applet class. The sound comes from a file. The second parameter, "click.au", is the name of the file that contains the sound. The first parameter, getCodeBase(), specifies the directory that contains the sound file. This parameter is actually a URL, but all you need to know is that getCodeBase() specifies that the sound file is in the same directory that contains the compiled class file of the applet. Unfortunately, not all sound files can be played by Java, and some sound files might work on one platform but not on another. A sound file with a name ending in ".au" is most likely to work. An interesting thing about the play() method is that it only starts the sound playing. It doesn't wait for the playback to finish. So, the applet continues to run while the sound plays. Here is a link that you can use to download the click sound, click.au.

The hardest part of this exercise is drawing the dice. I made each die 35 pixels wide, leaving a 10 pixel border on each side and 10 pixels between the dice. The top left corner of the left die is at (10,10), the top left corner of the right die is at (55,55). The 55 includes the 10 pixel border on the left, the 35 pixel width of the other die, and the 10 pixels between the dice. The paint method calls a drawDie() routine to draw each die, using the commands:

                drawDie(g, die1, 10, 10);
                drawDie(g, die2, 55, 55);

where die1 is the numerical value shown on the first die and die2 is the numerical value of the second die.

As for the drawDie routine, there are two quite different algorithms that could have been used for drawing the dots. Either:

              if the value shown is 1
                  draw 1 dot (in the center)
              else if the value shown is 2
                  draw 2 dots (in the top-left and bottom-right corners)
                .
                .
                .
              else if the value shown is 6
                  draw 6 dots (along the left and right edges)

Or:

              if the value has a dot in the top-left corner
                   draw the top-left dot
              else if the value has a dot in the top-right corner
                   draw the top-right dot
                 .
                 .
                 .
              else if the value has a dot in the bottom-right corner
                   draw the bottom-right dot

Although the first algorithm is more obvious, the second requires much less typing. (The first algorithm ends up using 21 drawOval() commands, while the second uses only 7.) Furthermore, after drawing the dice on paper, I found that the conditions for testing when a given dot needs to be drawn are simpler than I expected. For example, the values that need a dot in the top-left position are all the values greater than 1. The algorithm leads to my drawDice() routine:

      void drawDie(Graphics g, int val, int x, int y) {
             // Draw a die with upper left corner at (x,y).  The die is
             // 35 by 35 pixels in size.  The val parameter gives the
             // value showing on the die (that is, the number of dots).
          g.setColor(Color.white);
          g.fillRect(x, y, 35, 35);
          g.setColor(Color.black);
          g.drawRect(x, y, 34, 34);
          if (val > 1)  // upper left dot
             g.fillOval(x+3, y+3, 9, 9);
          if (val > 3)  // upper right dot
             g.fillOval(x+23, y+3, 9, 9);
          if (val == 6) // middle left dot
             g.fillOval(x+3, y+13, 9, 9);
          if (val % 2 == 1) // middle dot (for odd-numbered val's)
             g.fillOval(x+13, y+13, 9, 9);
          if (val == 6) // middle right dot
             g.fillOval(x+23, y+13, 9, 9);
          if (val > 3)  // bottom left dot
             g.fillOval(x+3, y+23, 9, 9);
          if (val > 1)  // bottom right dot
             g.fillOval(x+23, y+23, 9,9);
       }

It took some care to figure out the numbers to use in the fillOval commands. The individual dots have a radius of 9 pixels. There are three rows of dots, which have a combined height of 27 pixels. That leaves 35-27, or 8 pixels for spacing. I use 3 pixels between the dots and the edge of the die, and 1 pixel between rows. This puts the tops of the rows at 3, 3+9+1, and 3+9+1+9+1, that is, at 3, 13, and 23. The columns use the same numbers. (If you believe that I got all this right the first time, I won't disillusion you!)


The Solution


    /*
       Shows a pair of dice that are rolled when the user clicks on the
       applet.  It is assumed that the applet is 100-by-100 pixels.  A clicking
       sound is played when the dice are rolled.
    */
    
    import java.awt.*;
    import java.awt.event.*;
    import java.applet.*;
    
    public class ClickableDice extends Applet implements MouseListener {
    
       int die1 = 4;  // The values shown on the dice.
       int die2 = 3;
       
    
       public void init() {
              // To initialize the applet, register the applet to listen
              // for mouse events on itself.  Also set a light blue
              // background color.
          addMouseListener(this);
          setBackground( new Color(200,200,255) );
       }
       
    
       public void paint(Graphics g) {
              // The paint method draws a blue border and then
              // draws the two dice.
          g.setColor( Color.blue );
          g.drawRect(0,0,99,99);
          g.drawRect(1,1,97,97);
          drawDie(g, die1, 10, 10);
          drawDie(g, die2, 55, 55);
       }
       
    
       void drawDie(Graphics g, int val, int x, int y) {
             // Draw a die with upper left corner at (x,y).  The die is
             // 35 by 35 pixels in size.  The val parameter gives the
             // value showing on the die (that is, the number of dots).
          g.setColor(Color.white);
          g.fillRect(x, y, 35, 35);
          g.setColor(Color.black);
          g.drawRect(x, y, 34, 34);
          if (val > 1)  // upper left dot
             g.fillOval(x+3, y+3, 9, 9);
          if (val > 3)  // upper right dot
             g.fillOval(x+23, y+3, 9, 9);
          if (val == 6) // middle left dot
             g.fillOval(x+3, y+13, 9, 9);
          if (val % 2 == 1) // middle dot (for odd-numbered val's)
             g.fillOval(x+13, y+13, 9, 9);
          if (val == 6) // middle right dot
             g.fillOval(x+23, y+13, 9, 9);
          if (val > 3)  // bottom left dot
             g.fillOval(x+3, y+23, 9, 9);
          if (val > 1)  // bottom right dot
             g.fillOval(x+23, y+23, 9,9);
       }
    
    
       void roll() {
             // Roll the dice by randomizing their values.  Tell the
             // system to repaint the applet, to show the new values.
             // Also, play a clicking sound to give the user more feedback.
          die1 = (int)(Math.random()*6) + 1;
          die2 = (int)(Math.random()*6) + 1;
          play(getCodeBase(), "click.au");
          repaint();
       }
       
    
       public void mousePressed(MouseEvent evt) {
             // When the user clicks the applet, roll the dice.
          roll();
       }
       
    
       public void mouseReleased(MouseEvent evt) { }
       public void mouseClicked(MouseEvent evt) { }
       public void mouseEntered(MouseEvent evt) { }
       public void mouseExited(MouseEvent evt) { }
       
    
    } // end class ClickableDice


[ Exercises | Chapter Index | Main Index ]