CS211 Homework 6

Due: Thursday, Nov 30, at the beginning of class

Watch this space for later updates.

Purpose

You are to create the GUI (Graphical User Interface) for a simple game involving colors.  The goal for this homework is to gain some experience with the Java GUI and with event-driven programming. 

What to hand in

We plan to grade the homework based on your electronic submission.  The hardcopy is required mainly for backup.

What to do

You are given the code for ColorGame.java; this is a small class for a color-guessing game.  When created, a ColorGame picks a random goal color.  The isMatch() method checks to see if a guess color matches the goal color. The goal color and the most-recent guess color can be accessed from outside the class in order to create a GUI for this game.  Colors are specified by giving their red, green, and blue components.  The range field (also accessible from outside the class) indicates the maximum size int that can be used to specify a color component.  Possible values for the range are 4, 8, or 16 depending on whether the user chose to start an easy, medium, or difficult game, respectively.  Your job is to create a GUI for this game.  Here is a picture of the GUI.

The idea is to move the sliders to make the color in the center square match the color surrounding it.

Most of the code that you will need to create is related to layout.  The event-handling code (for the listeners) is about 80 lines long, including comments (and blank lines inserted for readability).  The total code for the entire game (including about 60 lines for the ColorGame class) consists of about 320 lines.

You need to create the code for ColorGamePanel which extends JPanel and contains the GUI for a ColorGame  The fields used by ColorGamePanel are described below.  The main program for ColorGamePanel is also given below.  The main program sets up a window (a JFrame) and places the ColorGamePanel within it.  You need to provide (1) a ColorGamePanel constructor that does the layout of the game and (2) appropriate listeners to make the game components function.

These are the fields used by (and defined within) ColorGamePanel.  You are welcome to introduce more fields if you find it necessary to do so.

JRadioButton easy,medium,difficult;	// Game level (4, 8, or 16 color-levels)
JSlider red,green,blue;			// Color controls
JPanel goalPanel;			// Where goal color appears
JPanel guessPanel;			// Where guess color appears
JLabel winLabel;			// Used to indicate winning
JLabel messageLabel;			// Where messages appear
ColorGame game;				// Current color game

The sliders and the radio buttons correspond to the obvious components shown in the picture above.  The goalPanel is the large outer square containing the goal color.  The guessPanel is the small inner square.  The winLabel does not appear above, but it shows up inside the guessPanel when someone wins the game (i.e., matches the goal color).  The messageLabel contains the message that appears at the bottom of the window.  The contents of the messageLabel change during the game in order to provide information to the player/user.  The game represents the current ColorGame.  Here's another picture of the game showing the winLabel in the guessPanel and showing a different message in the messageLabel.

Provided Code

The code for ColorGame is here.  Be sure to look at the main program and the code fragments that appear in the Hints below. 

Hints

  1. The winLabel is added to the guessPanel when the game is won and is then removed from the guessPanel when a new game is started.  Here's code to set up the winLabel.
    // Prepare the win label
    winLabel = new JLabel("Winner!");
    winLabel.setForeground(Color.black);
    winLabel.setOpaque(true);
    winLabel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
    The foreground color is set to black; otherwise it uses the default color (dark blue).  The setOpaque() method ensures that the label's background color shows up; otherwise the "Winner!" text might be invisible against a dark goal color.  The BorderFactory is used to make a border around the label; otherwise the label's outline fits tightly around the text and it looks funny.
  2. Java provides java.swing.ButtonGroup, a way to make the radio buttons act like buttons on a car-radio (i.e., only one at a time can be chosen).  Here's the code that sets up a ButtonGroup for the easy, medium, and difficult buttons.
    // Place the buttons in a ButtonGroup (only one at a time is on)
    ButtonGroup group = new ButtonGroup();
    group.add(easy);
    group.add(medium);
    group.add(difficult);
  3. The ColorGamePanel extends JPanel and is using BorderLayout.  You need to figure out how to place the sliders on the WEST, the radio buttons on the NORTH, etc. The guessPanel is inserted into the center of the goalPanel by using a GridLayout.  The other cells of this grid are filled with empty JLabels.
  4. The sliders will not be stretched to fill up their panel unless you use GridLayout.  Here is code for creating a slider like the ones pictured above.
    JSlider slider = new JSlider(JSlider.VERTICAL,0,1,0);
    slider.setMajorTickSpacing(1);
    slider.setPaintTicks(true);
    slider.setSnapToTicks(true);
    slider.setBackground(color);
    slider.setBorder(BorderFactory.createLineBorder(this.getBackground(),5));
    There are three methods that are used here to handle the tick marks: setMajorTickSpacing, setPaintTicks, and setSnapToTicks.  You should look up these methods in the API to see what they do.  The background color is set to Color.red, Color.green, or Color.blue, as appropriate.  The setBorder method is used to make some space around the slider using the background color of ColorGamePanel.
  5. You need to use addActionListener to add an ActionListener to each button.  An ActionListener has a method with the signature public void actionPerformed (ActionEvent e).  This method should determine which button was pressed (use e.getSource()); this determines how many color values (4, 8, or 16) are used for each color component.  This information is then used to set the maximum value for each slider (3, 7, or 15, respectively) using slider.setMaximum(int). You have to be a bit careful here when changing the slider maximums since any change to a slider generates a ChangeEvent; without care the ChangeEvent can cause an invalid response since the ColorGame is in the middle of being changed.

    The actionPerformed() method must also create a new ColorGame.  Note that the number of color values (4, 8, or 16) must also be passed to the ColorGame constructor so that an appropriate goalColor can be chosen. 

  6. You need to use addChangeListener to add a ChangeListener to each slider.  A ChangeListener has a method with the signature public void stateChanged (ChangeEvent e).  This method should determine the value for each slider by using slider.getValue().  These values can be used as the arguments to ColorGame.isMatch().  ColorGame.guessColor is then used as the new background color of the guessPanel.  You can tell when the user stops moving the slider by using slider.getValueIsAdjusting(); this method returns true as long as the user is still adjusting the slider.  Once the user is done moving the slider, you should check to see if the current color matches the goal color.  If this is the case then you can change the text of messageLabel and add winLabel to the guessPanel.  When a new game starts, you will need to execute guessPanel.remove(winLabel) to get rid of the winLabel again.
  7. Your ActionListener(s) and your ChangeListener(s) can be either inner classes or anonymous inner classes.  Note that they should not be static nested classes.  A static nested class has no access to the class's fields except for static fields.  Inner classes (either anonymous or named) do not use the static class modifier.  Such classes have access to the fields of the containing instance.
  8. It should be possible for the user to "cheat" during the game by clicking on the guessPanel.  This changes the messageLabel so that it displays information about the current guessColor and goalColor.  Here is code for doing this that uses an anonymous inner class.
    // Listen for mouse clicks on the guess panel (cheating)
    guessPanel.addMouseListener(new MouseAdapter () {
    	public void mousePressed (MouseEvent e) {
    		messageLabel.setText("Guess: " + game.guessColor 
    			+ "     Goal: " + game.goalColor);
    		}
    	public void mouseReleased (MouseEvent e) {
    		messageLabel.setText("You cheated!");
    		}
    	});
  9. Here is the main program for ColorGamePanel.
    public static void main (String[] args) {
    	JFrame frame = new JFrame("Color Matching Game");
    	frame.addWindowListener(new WindowAdapter () {
    		public void windowClosing (WindowEvent e) {System.exit(0);}
    		});
    	frame.setSize(500,400);
    	frame.getContentPane().add(new ColorGamePanel());
    	frame.setVisible(true);
    	}

    The code within addWindowListener sets up a WindowListener so that the window will disappear and the program will halt when the window-close box is clicked.  The rest of the code creates a new ColorGamePanel, adds it to the frame and displays the frame.

  10. A simple way to initialize the GUI: easy.doClick().  This generates an ActionEvent just as if the user clicked on the easy button.