Module 6: Loops
An example
We will use a simple example to illustrate loops:
- First, look at the applet.
- The applet simply draws an 8 x 8 grid, suitable for a board
game or for simply being bored.
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:
- Notice how the pattern repeats. If we had to draw a 100 x 100
grid, we would have to type hundred's of lines of code.
- A loop is a programming construct that helps repeat some
action, when each repetition involves some change.
- There are many ways of writing loops. Here's one:
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
public class GridDrawingAppletForLoop extends Applet {
public void paint (Graphics g)
{
// Draw the vertical lines:
for (int x=0; x<=240; x+=30) {
g.drawLine (x,0, x,240);
}
// Draw the horizontal lines:
for (int y=0; y<=240; y+=30) {
g.drawLine (0,y, 240,y);
}
}
}
Note:
- Notice how much shorter the program is. The program length would be
the same if we drew a 100 x 100 grid.
- for is a reserved word.
- The structure of the for statement looks like this:
for ( starting condition; continue-condition; change ) {
body of loop - a bunch of statements
}
- Thus, the way to "read" the first loop is:
"for x starting at zero, as long as x <= 240, increment x by 30 at
each iteration of the loop".
- Notice the use of the += operator. The first loop is
equivalent to:
// Draw the vertical lines:
for (int x=0; x<=240; x=x+30) {
g.drawLine (x,0, x,240);
}
- We could add some whitespace for additional clarification:
// Draw the vertical lines:
for (int x = 0; x <= 240; x = x + 30) {
g.drawLine (x,0, x,240);
}
- For loops almost always have loop "counters" like the variable
x in the loop above:
- Such a variable is declared in the for statement itself.
- The variable can be used in statements in the body
of the for-loop (as x is used above).
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:
- The general structure of a while loop looks like this:
set initial value
while (while-loop condition) {
body of while loop - a bunch of statements
}
- Notice that the "loop counter" x in the first
loop must be declared outside the while loop.
- When to use for versus while?
- Usually for-loops are ideal when a single counter increases
or decreases.
- A while-loop is better for cases when the terminating
condition is complicated, or a combination of terms.
- Generally, prefer a for-loop to a while-loop, if
a for-loop can be written easily.
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:
- See the applet
- The program:
import java.awt.*;
import java.applet.Applet;
import java.awt.event.*;
public class PieApplet 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;
for (int slice=1; slice <= numSlices; slice++) {
// 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);
}
} // end-for
}
}
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.