CS 5 Fall 2009
Lab Assignment #2
Due Wednesday, October 28

The purpose of this assignment is to consolidate your knowledge of Java objects, arrays, and event-driven programming by writing a fun graphics program.

Bugs!!

Now that you've got some programming experience under your belt, you're familiar with bugs. Of course, there's the other kind. You know, the kind that used to run rampant in the kitchens of several local restaurants. Fortunately, each of these restaurants has either cleaned up its act or gone out of business.

Write an applet, Bugs, that simulates these erstwhile kitchens. Wherever you double-click, a bug appears and moves toward the top of the window. If you single-click on top of a bug, the bug disappears. (If the location of a single-click is over more than one bug, then all the bugs under the click location disappear.) The applet window should display the number of bugs at all times. You can play with my implementation:

Note also that the action stops when the mouse leaves the applet window, and it resumes when the mouse enters the applet window.

Specifics

You will of course use final variables for all the constants in your program. Right?

The applet window should be 500 x 500 pixels, with a pink background. (Use Color.pink.)

Wherever you double-click gives the upper left corner of the image of a bug that appears and moves up toward the top of the window. When you single-click, all bugs whose images are under the mouse location are terminated. That is, they no longer exist.

Your applet should be able to accommodate up to 500 different bugs at one time. Note that once a bug moves off the top of the applet window, it no longer exists: it doesn't contribute toward the count of bugs, and your program should no longer keep track of it. Each bug should move 3 pixels toward the top of the applet window every 100 milliseconds.

The image of the bug is at bug0.gif. It looks like this:

This image is 33 pixels wide and 50 pixels high. In order to run the applet, you will need to put this image into the project that contains your .java files. Just drag the image onto the src or (default package) icons or names, the same as you do for your .java files. Its name will appear after your .java files.

How to read and display an image

Because the Bugs program animates the bug0.gif image, you need to know how to read this image and how to display it.

The Applet class has a method, getImage, that you will use to read the image. Because this method is in the Applet class, you can call it in any of the methods within your class that extends Applet. For example, my implementation calls getImage in the init method.

getImage takes two parameters. The first is a reference to a URL object. Don't worry about what a URL object is. You'll use a call to the parameterless method getCodeBase as the first actual parameter in your call to getImage; getCodeBase returns just what you need. The second parameter to getImage is a reference to a String containing the name of the image file you want to read; you'll use "bug0.gif" as this parameter. getImage returns a reference to an Image object. You'll use this Image object to draw the image.

For example, you could write the following in the init method of your applet:

Image bugPicture = getImage(getCodeBase(), "bug0.gif");

Once you have a reference to an Image, you display it with the Graphics method drawImage. This method takes four parameters: a reference to the Image to display, the x-coordinate of the upper left corner of where the image is to appear, the y-coordinate of the upper left corner, and a reference to the Applet object in which it will be drawn. (I have no idea why this last parameter is necessary.) If you have the following declarations, here's how you would display an image:

Graphics page; Image bugPicture; int x, y; Applet theApplet; ... page.drawImage(bugPicture, x, y, theApplet); I'll have more to say about displaying the bug image a little later.

How I wrote the program

Here I'll tell you how I wrote the program. You do not have to design yours exactly as I designed mine, but you are required to satisfy certain requirements. I'll make clear what you must do. If I don't say that you must do something a certain way, you don't have to.

The Bug class

When I wrote the program, I first defined a class Bug. Each Bug object has only one instance variable, a reference to a Point, giving the upper left corner of where the Bug's image is currently displayed.

In addition to the instance variable, I defined several static variables. Recall that there is only one version of a static variable, and it is shared among all instances of the class.

I wrote a static method that initializes these latter two static variables. The init method of the applet itself calls this static method, passing it a reference to an Image that it reads, and a reference to the applet itself (think: this). Why bother? I needed these two references within the method that draws a Bug in order to call drawImage. I suppose I could have passed them around as parameters, but I know that it's going to be the same Image and the same applet the whole time. Why not just get them once and for all at the start and use them as needed?

The constructor for my Bug class just takes the Bug's initial position and saves it in the instance variable.

My Bug class has a method that reduces the y-coordinate by subtracting the speed, and it also returns a boolean indicating whether any part of this image is still visible. That's easy to compute: we know the y-coordinate of the upper left corner, and we know the height of the image, and we just want to know whether the y-coordinate of the bottom of the image is nonnegative.

My Bug class also has a method that just determines whether a Point, given as a parameter (technically, a reference to the Point, but you know that...) is within the image of the Bug. Again, this is pretty easy. You've got the Point, you've got the upper left corner of the image, and you've got the width and height. To be within the image, the Point has to be

All four of these conditions must be met.

The Swarm class

One bug does not an infestation make. So I next created a class that maintains information about many bugs (up to 500 at a time). You can call this class whatever you like; I called mine Swarm.

My Swarm class has two instance variables: an array of references to Bug objects, and a count of how many bugs there currently are. If there are n bugs, then the first n array entries reference the appropriate Bug objects, and the rest are null.

Although you could use an ArrayList instead of an array, you are required to use an array—not an ArrayList—of references to Bug. This array must follow the rule above: If there are n bugs, then the first n array entries reference the appropriate Bug objects, and the rest are null.

Here's a brief description of the methods of my Swarm class:

The Bugs class

Finally, I made a Bugs class as the applet. You know how to write most of it, and I've given you some hints above. What you don't yet know is how to distinguish single-clicks from double-clicks. You will need to do so in your mouseClicked method.

Given a reference, say event, to a MouseEvent, you can call the MouseEvent method getClickCount to get how many mouse clicks occurred in the event. The call event.getClickCount() returns the number of clicks as an int. All you need to do is to terminate bugs whose image contains the mouse position upon getting one click, and to add a new bug at the mouse position upon getting two clicks.

There is one slight subtlety. It turns out that a double-click event is always preceded by a single-click event. How do you know not to terminate bugs when a double-click occurs? You don't. We'll finesse this problem by calling it a feature rather than—I've painted myself into a corner here—a bug. Don't feel bad for the bugs just because we might terminate some in order to create one. Remember how many insects there are in the world—enough that we would need 64 bits to count them.

Debugging

The usual tendency is to write all the code and then to try to run it. This strategy almost never works, and when something fails you have to look at your whole program to try to figure out what went wrong. It seems like more work, but actually saves time in the long run, to implement this assignment "bottom up." Implement the Bug class and immediately test it. To test it, you will have to write a small driver applet that creates some Bug objects and runs the Bug methods on them. (Remember that "driver" is not synonymous with "main method." Rather, a driver is a program that exists just to exercise some other piece of code. Here, the driver I am suggesting exercises your Bug class.) Make sure that the bugs draw and move correctly. Test to verify that your code correctly determines whether a given Point is within a Bug's image.

Next implement the Swarm class and test it. Make sure that you can add bugs, move bugs, draw bugs, and that bugs are terminated correctly when they run off the top of the applet or when they are under a given point. (It may help to reduce the maximum number of bugs to a small enough value that testing is easy. Yay for final variables!)

Next implement the init, paint, and mouseClicked methods. Don't worry about the timer yet. Make sure that bugs are added and drawn when you click the mouse.

Finally, add the Timer object and then actionPerformed, mouseEntered, and mouseExited methods.

Extra Credit

Here are some ideas for extra credit. You are welcome to try out your own ideas, but you should discuss your own extra-credit ideas with me in advance so that I can determine whether they're worthwhile.

If you hand in any extra credit, make sure to hand it in as a program separate from the basic part of this lab assignment. That way, if you make any errors that are due solely to the extra-credit part, you won't lose points for the basic part of the assignment.

Moving bug legs

The applet so far has the bugs moving toward the top of the applet window, but the bugs' legs don't move. That ain't natural! Instead, you can cycle through a sequence of bug images: bug0.gif, bug1.gif, bug2.gif, bug3.gif, bug4.gif, bug5.gif, bug6.gif, bug7.gif. Here's what this sequence looks like:

After the last image (bug7.gif), you can cycle back to the first image (bug0.gif). (If you're astute, you'll notice that bug1.gif and bug3.gif are the same image, and bug5.gif and bug7.gif are the same image. That won't help you, however.)

Clearly, you'll need to read in an array of Image objects. Each Bug object will have to keep track of which of these images it's currently displaying. They all have the same size: 33 pixels wide and 50 pixels high.

Variable-speed bugs

Not all bugs have to move at the same speed. Make it so that each bug moves at its own speed, so that one bug might move 3 pixels each time but another might move 7 pixels each time. I did this by using a random number generator to decide how fast each bug moves at the time the bug is created, and the speed of each bug is now an instance variable in the Bug class, rather than a static variable.

Here's my applet with both of the above extra-credit features:

Bugs in all directions

My bugs go only toward the top of the applet window. Make yours go up, down, left, or right. You'll need to rotate and flip the .gif files accordingly.

Raid!!

Since a single-click terminates bugs, it's like spraying Raid. Animate a red circle that grows and shrinks upon each single-click, so that it looks like a point of impact.

General directions for turning in your homework

I hope you're getting the hang of this.
  1. Turn in a clean listing (i.e., a printout of your source code) of your program.

  2. Hand in two shots of the applet window with bugs in various locations. The second shot should be taken shortly after the first shot, so that we can see that some bugs have been terminated (because they ran off the top of the applet) and that other bugs have moved.

  3. Send a Blitz to the special homework account for your section leader containing your Java files.

  4. If the timestamp on your Blitz is after 1:45 PM on Wednesday, October 28, there will be an automatic 16-point deduction. If the timestamp is after 5:00 PM on Thursday, October 29, we will not accept your assignment and you will get a 0.

  5. Package the listings and output together, all clearly marked and containing your name. Put them in an envelope that is at least 8.5 x 11 inches, and don't forget to put your name and your section leader's name on the outside of that envelope as well. We will not accept your assignment if it is not in an envelope. If you cannot afford an envelope, please see me or a TA, and we will get you one. Put the envelope in the HW HANDIN box of your section leader by the start of class on October 28. If you hand it in after the start of the class on October 28, you will need to get it directly to your section leader. If you Blitz your electronic version on time but your envelope is late, there will be a 1-point deduction if we get it by 5:00 on Wednesday, or a 2-point deduction if we get it by 5:00 on Thursday. After 5:00 on Thursday, we will not accept it, and there will a 6-point deduction.

    It is your responsibility to round up the envelope before the assignment is due.

Grading

Your section leader will run your program and grade this assignment looking for the following things:
Back to Lab Assignments
Thomas H. Cormen <thc@cs.dartmouth.edu>
Last modified: Mon Oct 19 21:28:11 2009