CSCI 470/680E-- Assignment 4 -- Fall 2009
See New Extra Credit at end of Part 1
In this assignment you will write a Java Swing GUI application that does two different things:
1) shows a graphical "bouncing shape" animation in one JPanel and
2) presents an Matching Game in the other JPanel
These two functions are entirely unrelated, but illustrate methods for creating modular plug-in-able custom components with complex behaviors for applications (and Applets). You will also use Threads, graphic methods, a simple polymorphic class hierarchy, and read a text file from disk.
The two parts will be implemented as two self-contained custom JPanels. The role of the application is (almost) entirely to create and add these two JPanels. So the application itself should only be a few lines long. Use a GridLayout of 1 row, 2 columns for the application; place one of the custom JPanels in each.
Note: All variables should be private or protected. If you forget, you will not be penalized unless you use "default = public" access; that is you should not do anything like
objref.dataMemberName = 3;
from code outside objref.
Part 1 - The Bouncing Animation

This animation will allow the user to create objects which, once created, continuously bounce around the screen. They move in straight lines according to their speed and direction until they hit a "wall" of the display surface (which we will call a "canvas", implemented as a custom JPanel) at which time they bounce off it like a tennis ball and continue in a new direction.
This JPanel will implement Runnable to run the animation in a separate thread of execution.
You will create a small Shapes class hierarchy as follows:
Shapes Hierarchy
-- an abstract class Shape which contains all the common variables and methods for the three subclasses. These will include at least variables for the position and size as well as the x- and y- components of the object's speed. In addition, you should include a boolean variable indicating whether the object will draw itself in a "plain" or "fancy" way. Include methods to move the object (i.e. update its position). Provide an abstract draw() method for the Shape class, which will be overridden in the subclasses as appropriate for each object type. Note that since draw() is a method of the Shape class and the Shape class will not inherit any graphics capability, draw() will need to be passed a Graphics argument to use in all drawing operations.
-- a class Rect which will draw itself as a small rectangle. The program will set the size. The "plain" drawing mode will be a simple rectangle. The "fancy" way is up to you; as a suggestion, draw a + inside the rectangle.
-- a class Circle which will draw itself as a small circle; again, the program will set the size. The "plain" drawing mode will be a simple circle. The "fancy" way is up to you; as a suggestion, fill the circle with a solid color.
-- a class Cross which will draw itself as two intersecting lines. Alternately, you can make up a different class (Triangle? Polygon?) The fancy way, again, is up to you.
User Interface Behavior
When the application starts, default values are displayed:
Note: trails will apply to all Shape objects: either all Shapes leave a trail or none do. The trails JCheckbox controls whether or not the screen is erased when the animation is running. Whenever the user clicks on this box, the on-screen behavior immediately changes, even as the animation is running. So trails are all immediately erased (for false) or all moving Shapes immediately begin leaving a trail (true).
On the other hand, each new Shape can be "plain" (unfilled) or "fancy" (filled).
Both Start and Stop are initially disabled. Only Create is enabled. When Create is pressed, and object of the type currently selected in the Object Type list should be created and displayed on screen at a random position and a size determined by your program (not too big, not too small). The speed and direction are set by obtaining the current values in the "x-speed" and "y-speed" TextFields. In addition, once Create has been pressed, Start is enabled. Pressing Start should start the animation and should then enable Stop and disable Start - so you can now stop the animation, but not try to start it a second time while it is running. Pressing Stop should stop the animation, enable Start and disable Stop - so you can't stop the animation if it's already stopped.
Keep the Shape objects in an ArrayList - each time the Create Button is pressed, create a new object of the specified type with the specified attributes and store it in the ArrayList. Where should the the ArrayList be declared? Probably in the custom Runnable JPanel (the canvas) that you create, since it should have direct access to (i.e. should "own") the objects it is in charge of moving around. So the control Panel's create should call a method of this Runnable JPanel to add the new object to the ArrayList.
The JPanel's run() method does the animation. It will display each object in the ArrayList. It will either erase the JPanel's area before (re-)drawing (if "Trails" is false) or not erase (if "Trails" is true). Then it will update the position of each object for the next iteration. The new position will be the object's old x-position plus the x-speed and the object's old y-position plus the y-speed. However, if the x position is off the Canvas, then set the x-speed to its negative value (2 becomes -2, etc.). Do the same for the y-speed. This will implement the bouncing behavior.
Any time your program is about to use the x-speed or y-speed values you should check for both invalid numbers ("a3d") and out-of-range values (we'll use -5...+5 for our limits). To help do this, write a utility method:
int getInteger(TextField tf, int min, int max)
which will get the contents of tf and try to convert it to an int; if it succeeds it will then test the number for being in-range. getInteger() can throw a NumberFormatException or a plain Exception. It will re-throw the NumberFormatException if the TextField contains an invalid number; it will throw a new Exception with a String argument ("err: " + min + "..." + max) if the number is out of range. In either case, the caller of getInteger() should catch the exception and handle it by either
New Extra Credit:
Add a menu to the application to give the user an additional way to control this animation. The original controls should remain functional as well. Your Menu should resemble the following:
Start Stop Create Speed Style Trails
o Fast [] Fill o on
o Medium o off
o Slow
Here, Start, Stop, and Create a JMenuItems added directly to the JMenuBar and so will act like a Button press (rather than displaying a drop-down menu). In the drop-down menus, Fill is a JCheckboxMenuItem and the others are all JRadioButtonMenuItems.
Part 2: Matching Game
This JPanel implements a simple "matching" game. 16 words are shown on the screen arranged in a random order. You must devise a method display the words in random locations in a 4 x 4 grid; each time the game is played, the words should appear in different locations (it is possible that - randomly - they might appear in the same locations, but it is highly unlikely.) Eight of the words are in one color, and another eight are in a different color. The two colors mean that each word in color 1 has a match showing in color 2 which the played must identify by clicking on it. The player will first click on a word in color 1, and then try to select and click on its match (one of the color2 words). If that choice is correct, the two words are replaced by a picture to signify that they have been matched and cannot be selected again.
The "match" pairs could be opposites (up, down) or States and Capitals (like Illinois and Springfield)...or rhyming words (like "code" and "toad")...or foreign language words (like "ano" and "what" - Tagalog)...or Java methods and classes (like init() and JApplet).
Although many of the implementation details will be left to your own design for this part of the assignment, here are some suggestions and some things that must be done.
Let each of the "word panels" be represented by an instance of a custom class that can be added to a Container and that can be painted upon.. In its paintComponent() method this class can draw a green border (in its insets area) which signifies that the word has not yet been clicked. It can also paint its interior in its color and draw its word (as a String) . So, at a minimum, you might want to pass the following arguments to its constructor: the color and the word-String. You will also find that it is necessary that each of these objects knows which other object represents its match. So you will also need to pass the match word (as a String), or as the opposite word's panel cell location (zero - fifteen), or as an object reference.
When the user first clicks on a word, change its insets border to a different color to indicate that one selection has been made. When the second word is clicked, also change its border color and then check to see if there is a match. If so, replace both words with the picture and somehow keep track of this fact so that they never change from displaying the picture. If this is not a match, set both words back to having their original green insets color. (Note: you may use your own image instead of the picture shown here.)

The picture shown is a small jpg. You can load a jpg from disk (place the picture file in the same directory as the program) via a Java Toolkit method as follows:
Image img = Toolkit.getDefaultToolkit().getImage(getClass().getResource("bonzai.jpg"));
then to display it on a subclass of JComponent, for instance, you can call drawImage():
g.drawImage(pic, 0, 0, d.width, d.height, this);
where d.height and d.width are available after calling getSize() for the JPanel.
There will be a "New Game" button. Clicking the "New Game" button will result in a completely new random arrangement of the 16 words, with a new set of two colors (one for the words, one for the matches). Although you may restrict yourself to, let's say, eight or nine sets of predefined colors that you randomly select from to get the background colors, the word arrangement itself should not be chosen from a predefined set of word arrangements. The arrangement should be completely determined at run time in a truly random fashion. Thus it is unlikely that one would ever see the same arrangement of words more than once.
Since each word panel descends from JComponent, it is easy to remove components and then add new ones when the "New Game" button is pressed. First you must invalidate() the current layout of the Container. Next, removeAll() components from the Container. Now, create your new set of word panel components and add them using the Container's add() method. Finally, validate() the Container to show the newly added components. That's it!
The words and their opposites should be read in from a text file that should reside in the same directory as your program. Pass the name of the file to read as a command line argument to the application. It would be best to pass this name to the constructor of the Quiz JPanel so the Quiz class can read and process this file It is suggested that you use a format similar to the following in your text file (although you are not required to do so):
// here is word set 1
UP
DOWN
// word set 2
IN
OUT
// word set 3
.
.
// word set 8
BEGIN
END
!!
When reading from the text file, any line that begins with // should be treated as a comment and thrown away. Do include the comment facility in your file and code. Note that here the two exclamation points (!!) serve as an end-of-file indicator. You are not required to use this.
You can create a BufferedReader using the FileReader class as its source-of-bytes argument:
BufferedReader input = new BufferedReader (FileReader(filename-as-String));
Now you can simply use the readLine() method of the BufferedReader class to read the text file one line at a time until end of file.