CPSC 124, Winter 1998
Sample Answers to Lab 7


This page contains sample answers to some of the exercises from Lab #7 in CPSC 124: Introductory Programming, Winter 1998. See the information page for that course for more information.


Exercise 1: Here is a completed "histogram" program, with the added code shown in blue.

            /* This file defines a "histogram" program.  The user is asked to
               enter some numbers.  The numbers are stored in an array.
               Then the data in the array are displayed in the form of a
               histogram. (For each number in the array, the histogram
               shows a line of stars.  The number of stars in the i-th line
               is equal to the number in the i-th spot in the array.)

               NOTE:  Your assignemnt is to complete the missing parts of
                      of the program.  You can use the stars() subroutine,
                      given below, to output each line of stars.
            */
               

            public class Histogram {

               static Console console = new Console();  // console for input/output

               public static void main(String[] args) {

                  int numberOfLines;  // The number of lines in the histogram.
                                      // This is also, therefore, the size of the array.
                  
                  int[] data;  // The array of data, containing numbers entered by the user.
                  
                  console.putln("This program will display a histogram based on data");
                  console.putln("you enter.  You will be asked to enter the numbers");
                  console.putln("that specify how many *'s there are in each row of");
                  console.putln("the histogram.  The numbers must be between 0 and 50.");
                  console.putln("");


                  // Find out how many numbers the user wants to enter.
                  // This number is restricted to be in the range 1 to 25.

                  do {
                      console.put("How many numbers do you want to enter? ");
                      numberOfLines = console.getlnInt();
                      if (numberOfLines < 1 || numberOfLines > 25)
                         console.putln("Please specify a number between 1 and 25.");
                  } while (numberOfLines < 1 || numberOfLines > 25);


                  // Create an array to hold the user's numbers.  The size of the
                  // array is given by the number of numbers the user wants to enter.

                  data = new int[numberOfLines];

                  // Use a for loop to read the user's numbers, one at a time, and
                  // store them in the array:

                  console.putln();
                  console.putln("Please enter your data:");
                  console.putln();

                  for (int i = 0; i < numberOfLines; i++) {  // get next number from user
                     do {
                        console.put("Item #" + i + "? ");
                        data[i] = console.getlnInt();
                        if (data[i] < 0 || data[i] > 50)
                           console.putln("Please specify a number between 0 and 50.");
                     } while (data[i] < 0 || data[i] > 50);
                  }


                  // Output the histogram, using a for loop.  Call the "stars()" method
                  // once for each number in the array:

                  console.putln();
                  console.putln("Here's a histogram of your data:");
                  console.putln();
                  console.putln("          10        20        30        40        50");
                  console.putln("|---------|---------|---------|---------|---------|-----");

                  for (int i = 0; i < numberOfLines; i++) {
                     console.put("|");
                     stars(data[i]);
                  }

                  console.putln("|---------|---------|---------|---------|---------|-----");

                  console.close();

               }  // end main();

               static void stars(int starCount) {
                    // Outputs a line of *'s, where starCount gives the
                    // number of *'s on the line.  There is a carriage
                    // return at the end of the line.
                  for (int i = 0;  i < starCount;  i++) {
                     console.put('*');
                  }
                  console.putln();
               }

            }  // end of class Histogram


Exercise 2: Here is the version of BBCanvas that I wrote for the enhanced version of the bouncing balls applet. Note the use of an array to store the color of each ball. The mouseDown() and mouseDrag() methods were added to tell the balls how to respond when the user clicks, or clicks-and-drags, the mouse.

            import java.awt.*;

            class BBCanvasEnhanced extends Canvas implements Runnable {

               int threadDelay = 40;   // Time, in milliseconds, inserted between frames.

               Image OSC = null;  // An "off-screen canvas", used for double buffering.
               Graphics OSG;      // A graphics object that can be used for drawing to OSC.
               int width, height; // The width and height of the canvas.

               boolean running = true;  // This state variable is "true" when the ball
                                        // should be moving, and "false" when it is 
                                        // standing still.

               double[] x;   // The left edge of the ball.
               double[] y;   // The right edge of the ball.

               double[] dx;  // The amount by which x changes from one frame to the next.
               double[] dy;  // The amount by which y changed from one frame to the next.

               Color[] ballColor;  // The color used for the ball.

               int ballSize = 10;            // The diameter of the ball.

               int ballCount = 30;


               BBCanvasEnhanced() {  
                      // Constructor merely sets the background color
                      // (Note:  The Canvas's width and height are not
                      //  established when the constructor is called,
                      //  so the "off-screen canvas" can't be created here.
                  setBackground(Color.black);
               }


               synchronized void initialize() {

                      // initialize() is called from the run() method, defined below,
                      // AFTER the off-screen canvas has been created ans the values
                      // of width and height have already been determined

                  x = new double[ballCount];  // create arrays
                  y = new double[ballCount];
                  dx = new double[ballCount];
                  dy = new double[ballCount];
                  ballColor = new Color[ballCount];

                  for (int i=0; i < ballCount; i++ ) {

                    x[i] = width / 2;   // Put the ball at the middle of the canvas.
                    y[i] = height / 2;

                    do {                            // Choose random velocity, not too small.
                       dx[i] = 20*Math.random() - 10;
                       dy[i] = 20*Math.random() - 10;
                    } while (Math.abs(dx[i]) < 2 && Math.abs(dy[i]) < 2);

                    if (i % 3 == 0)                // Assign one of three colors to each ball
                       ballColor[i] = Color.red;
                    else if (i % 3 == 1)
                        ballColor[i] = Color.blue;
                    else
                       ballColor[i] = Color.green;
                  }

               }


               synchronized void doTimeStep() {

                     // doTimeStep() is called over and over by the run() method
                     // It updates variables, redraws the offscreen canvas, and
                     // calls repaint() to make the changes visible on the screen.

                  if (running == false)  // If the "running" state variable is false,
                     return;             // then don't do anything.

                  OSG.setColor(Color.black);        // fill off-screen canvas with black
                  OSG.fillRect(0,0,width,height);

                  for (int i = 0; i < ballCount; i++) {
                     // First, move the ball by adding dx to x and adding dy to y:

                     x[i] += dx[i];
                     y[i] += dy[i];

                     // Next check whether the ball has hit one of the edges:
              
                     if (x[i] <= 0)           // Ball has hit left edge.
                        dx[i] = Math.abs(dx[i]); // Make sure ball is moving right (with dx > 0)
                     else if (x[i] + ballSize >= width)  // Ball has hit right edge.
                        dx[i] = -Math.abs(dx[i]);           // Make sure ball is moving left.

                     if (y[i] <= 0)           // Ball has hit top edge.
                        dy[i] = Math.abs(dy[i]); // Make sure ball is moving down (dy > 0).
                     else if (y[i] + ballSize >= height) // Ball has hit bottom edge.
                        dy[i] = -Math.abs(dy[i]);           // Make sure ball is moving up.
             
                     OSG.setColor(ballColor[i]);          // draw the ball (in its current location)
                     OSG.fillOval((int)x[i], (int)y[i], ballSize, ballSize);

                  }

                  repaint();  // Call repaint() to tell system to redraw screen

               }  // end doTimeStep()



               // -- Methods called from the applet in response to user actions, --
               // -- such as clicking on a button or checkbox.                   --


               synchronized void faster() {
                     // This method makes the ball go faster by multiplying its
                     // speed by 1.25.  (This is called from the applet.)
                  for (int i=0; i < ballCount; i++) {
                    dx[i] = 1.25 * dx[i];
                    dy[i] = 1.25 * dy[i];
                  }
               }


               synchronized void slower() {
                      // This method makes the ball go slower by multiplying its
                      // speed by 0.8.  (This is called from the applet.)
                  for (int i=0; i < ballCount; i++) {
                    dx[i] = 0.8 * dx[i];
                    dy[i] = 0.8 * dy[i];
                  }
               }


               synchronized void setRunning(boolean newRunning) {
                    // Set the state variable "running" to the specified new value.
                    // The ball stops moving when running is false.
                    // This method is called from the applet.
                  running = newRunning;
               }


               synchronized public boolean mouseDown(Event evt, int a, int b) {
                       // Called when the user presses the mouse button at
                       // the point (a,b).  Here, I adjust the velocities of
                       // all the balls so that they head towards (a,b).
                       // The speed of the ball does not change; it just
                       // changes direction.
                  for (int i = 0; i < ballCount; i++ ) {
                      double v = Math.sqrt(dx[i]*dx[i] + dy[i]*dy[i]);  // speed of ball
                      if (v > 0 && (x[i] != a || y[i] != b) ) {
                          dx[i] = (a - x[i]); // set velocity to point to (a,b)
                          dy[i] = (b - y[i]);
                          double d = Math.sqrt(dx[i]*dx[i] + dy[i]*dy[i]);  // length of (dx,dy)
                          dx[i] = (dx[i]/d)*v;  // adjust speed to be v, the same as it was before
                          dy[i] = (dy[i]/d)*v;
                      }
                  }
                  return true;
               }

               public boolean mouseDrag(Event evt, int a, int b) {
                      // Called when mouse is moved to a new position, while
                      // the user is holding down the mouse button.  Here, I
                      // just want to do the same thing as in mouseDown, so I
                      // call that method.
                  mouseDown(evt,a,b);
                  return true;
               }

             


               //------------------------ painting ------------------------------
               //
               // This should not have to be changed.

               synchronized public void paint(Graphics g) {  // Copies OSC to the screen.
                  if (OSC != null)
                     g.drawImage(OSC,0,0,this);
                  else {
                     g.setColor(getBackground());
                     g.fillRect(0,0,size().width,size().height);
                  }
               }

               public void update(Graphics g) {  // Simplified update method.
                  paint(g);                      // (Default method erases the screen
               }                                 //       before calling paint().)

               //-------------------- thread stuff and run method -----------------
               //
               // It shoud not be necessary to change this.

               private Thread runner;
               
               void startCanvas() {  // This is called when the applet starts.
                  if (OSC == null) { // Off-screen canvas must be created before starting the thread.
                     width = size().width;
                     height = size().height;
                     OSC = createImage(width,height);
                     OSG = OSC.getGraphics();
                     OSG.setColor(getBackground());
                     OSG.fillRect(0,0,width,height);
                  }
                  if (runner == null || !runner.isAlive()) {  // create and start a thread
                     runner = new Thread(this);
                     runner.start();
                  }
               }

               void stopCanvas() {  // This is called when the applet is stopped
                  if (runner != null && runner.isAlive())
                     runner.stop();
               }

               void destroyCanvas() {  // This is called when the applet is destroyed.
                  if (runner != null && runner.isAlive())
                     runner.stop();
                  runner = null;
               }

               public void run() {  // The run method to be executed by the runner thread.
                  initialize();
                  while (true) {
                     try { 
                        Thread.sleep(threadDelay);
                     }
                     catch (InterruptedException e) { }
                     doTimeStep();
                  }
               }  // end run()

            }


Exercise 3: Arrays make it possible to deal with many items, without declaring a variable for each individual item. Since an array can be processed with a for loop, a few lines of code can easily be written to apply the same operation to each item. For example, where it took two lines of code to increase the speed of a ball in the original BouncingBalls applet:

            dx = 0.8*dx;
            dy = 0.8*dy;

it takes only one more line of code to handle all the balls in the version that uses arrays:

            for (int i = 0; i < ballCount; i++) {
               dx = 0.8*dx;
               dy = 0.8*dy;
            }

Arrays are useful in cases where many items are input and must be remembered individually for processing later in the program. If you want to input a bunch of numbers from the user and add them up or find the maximum, you don't need arrays, since the numbers can be processed one-by-one as they are read. However, in the histogram example from this lab, the numbers input by the user are read into an array and saved. There is no other easy way to write this program.


David Eck, 3 March 1998