Lab 7
Cat and Mouse

Out October 17, due by midnight, Friday October 21

Download the project file

Introduction

This is a two-part lab. For the first part, you will be working with the Model/View/Controller paradigm, and building a very simple GUI interface. For the second part, you will work with another classmate to build a networked application based on your first week's project.

The first week emphasizes the following topics:

You should read through the entire assignment before beginning any of it.

As usual, you are encouraged to collaborate on this assignment. Hopefully by now you've found someone in the class that you feel comfortable working with. The second part of this lab will require close collaboration with another person, so you should think about who you'd like to work with and practice this week. Of course, your final work should be your own, and you must acknowledge the contributions of your blah blah blah. You've done this before. Just include the names of the people you worked with for this assignment, and how you collaborated.

This assignment is due by midnight on Friday, October 21, to sd-psets@lists.olin.edu, or as hardcopy to Katie.


Contents


Overview

In this lab, you are going to create a very simple game. You'll use your mouse (the pointing device) to guide a Mouse (a gray circle) from its square mousehole to a block of cheese. The Mouse will appear when your mouse touches the mouse hole, and disappear again when you touch the cheese. Yes, I know. It's not very interesting. But the framework you set up will enable you to turn your program next week into a networked game of tag. Now doesn't that sound cool?

We'll try to be disciplined as we design this project, and use the Model/View/Controller technique, which separates the information about an object from its presentation to the user. The model holds the simple data representation of an object, and typically contans some simple methods to manipulate that data. There can be more than one class that makes up the model. The view holds a reference to the model, and uses the model's data to display information on the screen. The controller listens for changes on the model to update the view, and listens for user events from the view to update the model.

We've written some of the code for you. Your job will be to write

The Model

There are three things displayed in this program: the Mouse, the Hole, and the Cheese. Each will have its own model, but we also need something that will hold them all together. We'll call it a World. The World object is the main model object, and it contains the methods that the View needs to display the state of the world, and that the Controller needs to manipulate the state of the world. For the most part, these are just methods to get access to the Cheese, the Hole, and the Mouse, but we've also thrown in something to keep track of a score for kicks. Unless you start adding code in the gravy section, you shouldn't need to change the World code.

As far as the View is concerned, there's not that much of a difference between a Mouse, a Hole and the Cheese. They're all just things that have a size and a location, and need to get drawn on the screen. To make life easier for the View, we can abstract the three displayable pieces into an interface. Let's call it, oh, say, Displayable.

The World has a method for pulling together all of the Displayable objects in a List and another method that gives the View an Iterator over the list so that it can draw all the parts.

public interface Displayable {
   public Rectangle getBounds();
   public void setLocation(int x, int y);
   public void paint(Graphics g);
}

Why do we have a setLocation method rather than individual setX and setY methods?
Hint: think back to the Etch-a-sketch lab. What might go wrong with individual setX and setY methods?

In fact, most Displayables will probably be identical except for the paint code. Just because we're feeling nice, we've written an AbstractDisplayable that implements the getBounds and setLocation methods for you. If you just want to have fun with graphics, you can extend this class and not worry about keeping track of the bounds.

Look closely at the code for AbstractDisplayable. Notice the constructor takes two int arguments. When you extend this class, you'll have to do something special in your new class's constructor. What is it? Write down the constructor for Mouse, assuming you want the Mouse to be 48 pixels wide and 40 pixels tall.

Drawing

You'll be writing three classes: one each for the Hole, the Cheese and the Mouse. Later on, you can go all out drawing pretty pictures, but initially, you should just use the simpler methods of the Graphics class. These include
fillRect(int left, int top, int width, int height) and
fillOval(int left, int top, int width, int height).

You'll also want to set a color before you do the drawing. The Graphics method is called setColor and it takes a Color object. The Color class defines several constants for colors, like Color.black and Color.yellow. You can also create brand new colors by specifying the red green and blue color values in the constructor for Color.

Are you feeling uncomfortable about putting drawing code into what's supposed to be a model? Why isn't the drawing code part of the view instead? In part, you can think of the drawing code part of the model as being just another type of data, like the model's position. Also, Remember that one of the purposes of having a separate model is so that multiple views can display the model data at the same time. It turns out that you still can show multiple views, because each View gives its own Graphics object to the paint method. Thus, the same model can draw the same image into different views.

The View

So now you've got a Displayable that would draw a nice gray blob if someone were to give it a Graphics and call paint. How does that happen? It happens in the View. If you look at the code, you'll notice that the View extends a JPanel. This means that whenever part of the window is exposed, or whenever anyone calls repaint() on the View, Java will schedule a paint call.

The paintmethod clears the panel by filling it with white, then steps through each Displayable, setting up the Graphics object and calling paint on the Displayable. If you look closely, you'll notice that the clip is set and the Graphics state is translated just before each Displayable's paint method is called. This is done so that the Displayable's paint method can only mark up the part of the screen within its bounds, and so that the top left corner of the Displayable's bounds looks to the paint method as if it were at (0, 0). After each paint method returns, the graphics is restored to its original state.

You may have noticed the extra check just before the Graphics is set up. Our View class checks to see if the Displayable it's looking at also happens to implement the Hidable interface. If so, and if the object wants to be hidden, the paint method skips that Displayable and doesn't paint it.

This means that if you want to hide the Mouse, you'll have to make the Mouse class also implement Hideable

What extra fields will you need for the Hidable interface? What methods are required? Write them down, and take a stab at writing the code to implement the Hidable interface before you come to lab.

The Controller

Finally, we come to the controller. In its current state, it creates a World and a View, and builds and shows a window to hold the View, but nothing much else happens. If you were to run the code without updating the controller, you'd see all three of your Displayables drawn on top of each other in the top left corner.

Why are they all up there in the top left corner? Can you find the place where the displayable is initialized? Explain why that initialization causes the display to be in the upper left corner.
What code would fix it so that each Displayable appeared in a random location? Write down two possible places to put the code for initializing the positions of the Cheese and Hole and explain why one of them is better than the other.

The controller doesn't pay any attention to the mouse at the moment. You need to implement a MouseMotionListener. See the Java API for the required methods.

Just for starters, ignore the Hole and the Cheese, and just write the MouseMotionListener method(s) so that the Mouse (the screen graphic) always follows the mouse (the pointing device).

Once you've written the methods, you'll need to register the listener with the object that produces the MouseMotion events. Think carefully about what object produces the events, and call its addMouseMotionListener method to add your listener.

Now you can rewrite the Controller methods so that the Mouse is hidden initially, is revealed when the mouse is inside the Hole, and gets hidden again when the mouse is inside the Cheese. For this, you'll want to look at the contains method of Rectangle.

This is the finishing target for this lab. What follows is just Gravy.

Gravy


Pre-Lab work

Read the entire lab, and go over the code to make sure you understand what everything does. Answer all the questions marked with a Q, and ask your potential partner about anything you don't understand.

Most of all, don't panic. In lab, there are only four classes you have to write, and three of them are nearly identical. Be sure that you come prepared to lab -- trying to understand the lab in the first two hours and trying to complete it in the third doesn't work very well.

In-Lab

In-lab, you'll be creating three Displayable classes and editing the Controller class. First, download the project files and open it with NetBeans. The project won't compile, because it is missing Mouse, Hole, and Cheese files. Write them, using simple shapes for now. Make each one about 40 pixels in size for clarity. Remember to draw each shape as if the top left corner of the bounds is at (0, 0). If you try to draw it anywhere else, it won't show up.

To test them (and to see them all at once instead of scrunched up in the top left corner, you'll have to fix the initialization issue. You can assign them to fixed locations, or make their locations be random.

Next, see if you can get anything to respond to the mouse movement. You can try printing someting out first (use System.out.println()) to make sure your listener is set up properly. Don't forget to tell the event producer about your listener! Once that works, make the Mouse location follow the mouse pointer.

Finally, hide and show the Mouse as the mouse pointer encounters the Hole and the Cheese.

When everything works, you can get checked out, or continue on to some of the gravy suggestions.

What to turn in

Your completed assignment should include: