Module 6: Loops


An example

We will use a simple example to illustrate loops:

Here's the program:
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*; 

public class GridDrawingApplet extends Applet {

  public void paint (Graphics g) 
  {
    // Draw the vertical lines:
    g.drawLine (0,0, 0,240);
    g.drawLine (30,0, 30,240);
    g.drawLine (60,0, 60,240);
    g.drawLine (90,0, 90,240);
    g.drawLine (120,0, 120,240);
    g.drawLine (150,0, 150,240);
    g.drawLine (180,0, 180,240);
    g.drawLine (210,0, 210,240);
    g.drawLine (240,0, 240,240);
    
    // Draw the horizontal lines:
    g.drawLine (0,0, 240,0);
    g.drawLine (0,30, 240,30);
    g.drawLine (0,60, 240,60);
    g.drawLine (0,90, 240,90);
    g.drawLine (0,120, 240,120);
    g.drawLine (0,150, 240,150);
    g.drawLine (0,180, 240,180);
    g.drawLine (0,210, 240,210);
    g.drawLine (0,240, 240,240);
  }
}

Now, we will re-write the above code using a loop:

Note:
In-Class Exercise: Modify the above applet to create a 20 x 20 grid, with finer spacing.
In-Class Exercise: Can both loops above be written using a single variable x? Explain.


Using a while loop

Another way of writing a loop is to use a while construct:

import java.awt.*;
import java.applet.Applet;
import java.awt.event.*; 

public class GridDrawingAppletWhileLoop extends Applet {

  public void paint (Graphics g) 
  {
    // Draw the vertical lines:
    int x = 0;
    while (x <= 240) {
      g.drawLine (x,0, x,240);
      x = x + 30;
    }
    
    // Draw the horizontal lines:
    int y = 0;
    while (y <= 240) {
      g.drawLine (0,y, 240,y);
      y = y + 30;
    }

  }
}
Note:
In-Class Exercise: Modify the above applet to create a 20 x 20 grid, with finer spacing, using while loops.
In-Class Exercise: Can both while loops above be written using a single variable x? Explain.


Another example

We will draw a pie with 20 alternately colored slices next:

Note:
  • We have used a for-loop to range over the number of slices.

  • The angle of each successive slice is updated inside the loop.

  • We have used the ++ (increment) operator to increment the variable slice each time through the loop.

Here's a while-loop version of the same:

import java.awt.*;
import java.applet.Applet;
import java.awt.event.*; 

public class PieAppletWhile extends Applet {

  public void paint (Graphics g) 
  {
    // The starting coordinates for each pie slice:
    int x = 50, y = 50;

    // The width and height of the containing rectangle for
    // a pie slice (Recall: this is how we draw arcs).
    int w = 100, h = 100;

    // Number of slices. We'll set it to 20, as an example.
    int numSlices = 20;
    
    // The angle of each slice:
    int degrees = (int) (360.0 / numSlices);
    
    // We will vary the start angle:
    int startAngle = 0;
    
    // Initialize counter of while loop:
    int slice = 1;

    // Loop:
    while (slice <= numSlices) {

      // First slice is different.
      if (slice != 1) {   
        //Inital value of startAngle must be 0
        startAngle = startAngle + degrees;               
      }

      if (slice % 2 == 0) {  
        g.setColor (Color.yellow);
        g.fillArc (x,y,w,h, startAngle, degrees);
      }
      else {
        g.setColor (Color.red);
        g.fillArc (x,y,w,h, startAngle, degrees);  
      }

      // Increment counter:
      slice ++;

    } // end-while

  }

}
In-Class Exercise: Modify the above applet (while or for-loop version) to add a textfield which reads in the number of slices.


Beware of infinite loops

It is easy to write down incorrect logic and make a program execute for ever:

  • Consider the while-loop version of the pie program above.

  • Suppose we made the following change:
        // Initialize counter of while loop:
        int slice = 1;
    
        // Loop:
        while (slice <= numSlices) {
    
          // ... other stuff the same ...
    
          // Forgot to increment counter
    
        } // end-while
      

  • What happens?
    • The variable slice never gets incremented.
    • The loop continues for ever.


Animation

One of the more interesting uses of a loop is to enable animation. Let's look at a simple example:

  • First, view the applet in action

  • Next, we need to understand how any kind of animation generally works:
    • An animation is a series of frames (still pictures).
    • If the frames are presented before the human eye in rapid succession, the illusion of animation is created.
    • There needs to be fast switching of frames (else, it'll look clumsy).
    • Each frame needs to be held for at least some time (else it'll go by too fast, and won't look like animation).

  • From an applet programmer's point of view:
    • We will draw frames one by one in the paint method.
    • We will assume that the drawing itself is so fast that it solves the "switching problem" above.
    • However, we will need to "hold" each frame for a short time. For this purpose, we have to have a way of making the program itself "stop" temporarily.

  • The applet below contains a number of new ideas, which we will explain after displaying the program.
Here's the program:
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;

public class BouncingBallApplet extends Applet implements Runnable {

  // How many milliseconds between frames:
  int delay;

  // The current frame:
  int frameNumber;

  // A thread object.
  Thread animatorThread;


  public void init ()
  {
    // We'll pause 500 ms between frames.
    delay = 500;

    // We are going to use "null" as a true/false indicator.
    // Need to initialize to "false" (null).
    animatorThread = null;
  }


  // Start is called by the browser each time the applet
  // comes into view.

  public void start ()
  {
    // Initially, we're at frame 0.
    frameNumber = 0;
    
    // If there isnt' an animator thread, create one.
    if (animatorThread == null) {
      animatorThread = new Thread (this);
    }

    // Start the independent thread.
    animatorThread.start ();
  }


  public void stop () 
  {
    // Stop the animation by indicating "false".
    animatorThread = null;
  }


  public void run ()
  {
    // Potentially, an infinite loop!
    while (animatorThread != null) {

      frameNumber ++;

      // Draw next frame.
      repaint ();

      // Delay
      try {
        Thread.sleep (delay);
      } 
      catch (InterruptedException e) {
        break;
      }
    }

  }


  public void paint (Graphics g) 
  {
    // Set foreground and background colors.
    setBackground (Color.white);
    g.setColor (Color.blue);

    // Here's how to get the size of the applet bounds within the program:
    // d.width is the width, d.height is the height.
    Dimension d = getSize();

    if (frameNumber % 2 == 1) {
      // On odd frames, draw the ball at the top.
      g.fillOval (0, 0, d.width/2, d.height/2);
    }
    else {
      // On even frames, draw the ball at the bottom.
      g.fillOval (0, d.height/2, d.width/2, d.height/2);
    }
  }

}
Note:
  • The first thing we will observe is that there are two new methods called start and stop:
    • These are methods special to applets.
    • The method start is called by the browser each time the applet comes into view.
    • Likewise, the method stop is called when the applet goes out of view.
    • The idea is, if the applet goes out of view, we should stop the animation.

  • The most important new idea is that of a thread:
    • Have you noticed how it's possible to simultaneously run multiple programs on your computer? For example, you could be downloading a big file, while simultaneously answering email by clacking on the keyboard.
    • In reality, the tiny processor inside your computer can only run instructions in strict sequential order.
    • However, by running a few instructions from one program, then a few instructions from another, and then switching back to the first, the illusion of simultaneity is created.
    • Now, as a programmer, we would like to handle multiple tasks simultaneously. For this purpose, the programming construct is a thread, as in a "thread of execution".
    • In an animation example, we want an animation thread to pause between frames, and we want another thread (the regular thread that runs the applet) to continue, so it can respond to events as needed.

  • In Java, it's relatively easy to work with threads;
    • Your class needs to implement the Runnable interface (see the line containing class).
    • You need to have a run method, in which you control the animation timing.
    • You pause a thread by executing Thread.sleep (delay) with a desired amount of delay.

  • Even if the above (terse!) explanation doesn't make sense, you can go ahead and use the above template to create animations: simply adjust the delay as desired and do your "drawing stuff" in the paint method.

  • Couple of minor new ideas in the program:
    • The while loop in run() is potentially infinite - it can run forever as long as the applet is not stopped.
    • We are using the existence of the thread itself as a "flag" to tell us when to stop.
    • There's new syntax, such as the try - catch statement.
    • You can get the size of the applet in your program by calling getSize().
In-Class Exercise: Play around with the delay in the applet. What happens if the delay is 50ms? What happens if it's 2000ms?
In-Class Exercise: Modify the applet so the ball sways horizontally.
In-Class Exercise: Add a second ball to the right of the first that's out of sync with the first (i.e., it's up when the first is down).


Listening to mouse clicks

Let's modify the bouncing ball applet to start and stop when the user clicks anywhere on the applet:

  • View the applet: click to stop the bouncing, click to start bouncing again.

  • The example will illustrate many ideas.
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;

public class BouncingBallApplet2 extends Applet implements Runnable, MouseListener {

  int delay;
  int frameNumber;
  Thread animatorThread;


  public void init ()
  {
    delay = 500;
    animatorThread = null;

    // Need to tell Java to call "us" when a mouseclick occurs.
    addMouseListener (this);
  }


  public void start ()
  {
    frameNumber = 0;

    if (animatorThread == null) {
      animatorThread = new Thread (this);
    }

    animatorThread.start ();
  }


  public void stop () 
  {
    animatorThread = null;
  }


  public void run ()
  {
    while (animatorThread != null) {

      frameNumber ++;

      // Draw next frame.
      repaint ();

      // Delay
      try {
        Thread.sleep (delay);
      } 
      catch (InterruptedException e) {
        break;
      }
    }

  }


  public void paint (Graphics g) 
  {
    // Set foreground and background colors.
    setBackground (Color.white);
    g.setColor (Color.blue);

    // Here's how to get the size of the applet bounds within the program:
    // d.width is the width, d.height is the height.
    Dimension d = getSize();

    if (frameNumber % 2 == 1) {
      // On odd frames, draw the ball at the top.
      g.fillOval (0, 0, d.width/2, d.height/2);
    }
    else {
      // On even frames, draw the ball at the bottom.
      g.fillOval (0, d.height/2, d.width/2, d.height/2);
    }
  }


  // This method gets called when mouse click occurs.

  public void mouseClicked (MouseEvent e) 
  {
    // Draw a small red dot where the click occurred.
    int x = e.getX ();
    int y = e.getY ();
    Graphics g = getGraphics ();
    g.setColor (Color.red);
    g.fillOval (x-5, y-5, 10, 10);

    // If no thread is running, we'll create one.
    if (animatorThread == null) {
      start ();
    }
    else {
      stop ();
    }
  }
  
  // We need to have these methods, but
  // don't actually have to do anything inside.
  public void mouseEntered (MouseEvent m) {}
  public void mouseExited (MouseEvent m) {}
  public void mousePressed (MouseEvent m) {}
  public void mouseReleased (MouseEvent m) {}

}
Note:
  • To listen for mouse-clicks:
    • The applet needs to implement the Mouselistener interface (see the class line of the applet).
    • This means the applet needs to have the five methods mouseClicked, mouseEntered, mouseExited, mousePressed, mouseReleased.
    • Not all need to have statements, only the ones were we choose to do something.
    • Since we are going to listen to mouse clicks, we write our stuff in mouseClicked.

  • The mouse-click coordinates can be extracted as shown.

  • To emphasize, we draw a small red dot at the place the click occurred.

  • Notice how you can draw in some method other than paint by getting the Graphics object.
In-Class Exercise: Modify the applet so the ball moves only while the mouse is pressed (held down) and stops when released.


Deliberate randomness

We will combine the grid drawing and bouncing ball in this way:

  • The ball will move about randomly in the grid.

  • View the applet

  • In this way, we'll learn something about how to create random numbers.
Here's the program:
import java.awt.*;
import java.awt.event.*;
import java.applet.Applet;

public class GridBallApplet extends Applet implements Runnable {

  // How many milliseconds between frames:
  int delay;

  // The current frame:
  int frameNumber;

  // A thread object.
  Thread animatorThread;

  // Track the current cell containing the ball.
  int currentRow = 0;
  int currentColumn = 0;

  public void init ()
  {
    // We'll pause 500 ms between frames.
    delay = 500;

    // We are going to use "null" as a true/false indicator.
    // Need to initialize to "false" (null).
    animatorThread = null;
  }


  // Start is called by the browser each time the applet
  // comes into view.

  public void start ()
  {
    // Initially, we're at frame 0.
    frameNumber = 0;
    
    // If there isnt' an animator thread, create one.
    if (animatorThread == null) {
      animatorThread = new Thread (this);
    }

    // Start the independent thread.
    animatorThread.start ();
  }


  public void stop () 
  {
    // Stop the animation by indicating "false".
    animatorThread = null;
  }


  public void run ()
  {
    while (animatorThread != null) {

      frameNumber ++;

      // Draw next frame.
      repaint ();

      // Delay
      try {
        Thread.sleep (delay);
      } 
      catch (InterruptedException e) {
        break;
      }
    }

  }


  public void paint (Graphics g) 
  {
    // Set foreground and background colors.
    setBackground (Color.white);

    // Draw a grid:
    g.setColor (Color.black);
    
    // Draw the vertical lines of the grid:
    for (int x=0; x<=240; x+=30) {
      g.drawLine (x,0, x,240);
    }

    // Draw the horizontal lines of the grid:
    for (int y=0; y<=240; y+=30) {
      g.drawLine (0,y, 240,y);
    }
    
    // Choose a random new row, wrapping around 
    // if necessary.
    int newRow = currentRow + 1;
    if (Math.random () < 0.5)
      newRow = currentRow -1;
    if (newRow > 7)
      newRow = 0;
    else if (newRow < 0)
      newRow = 7;
    
    // Choose a random new column, wrapping around 
    // if necessary.
    int newColumn = currentColumn + 1;
    if (Math.random () < 0.5)
      newColumn = currentColumn -1;
    if (newColumn > 7)
      newColumn = 0;
    else if (newColumn < 0)
      newColumn = 7;
    
    // Compute where the top left corner of the ball's
    // bounding square should be.
    int x = newRow * 30;
    int y = newColumn * 30;

    // Draw it.
    g.setColor (Color.blue);
    g.fillOval (x, y, 30, 30);

    // Update current cell coordinates.
    currentRow = newRow;
    currentColumn = newColumn;
  }

}
Note:
  • Our grid is the same 8 x 8 grid from before, with 64 cells.

  • The rows are numbered 0,...,7 and the columns are numbered 0,...,7. Thus, the bottom-right cell has coordinates (7,7).

  • We now have two variables currentRow and currentColumn to keep the current position of the ball.

  • Aside from declaring the two variables at the top, all the changes are in paint.

  • A call to Math.random() returns a random number between 0 and 1.0.

  • Notice that we pick a new neighboring cell at random by generating a random row (top or below) and a random column (left or right).

  • If the new cell threatens to go off-grid, we wrap around.
In-Class Exercise: In the above applet, the row and column always change. Modify the applet so that one might also randomly elect to stay in the current row or current column.
In-Class Exercise: Modify the applet to draw a blue ball in the new position and a light gray ball in the old position.