Boggle-Board UI

This week you will begin working on the user interface for your Boggle game. There are two classes to concentrate on this week; one is a "Boggle button" that displays a single letter of the Boggle board, and the other is the "Boggle board" class that manages the display of the entire board.

To keep your life simple, you should build your UI classes off of existing Swing classes, and just extend their capabilities as needed. Your Boggle-button class should derive from javax.swing.JButton, and your Boggle-board class, being a collection of Boggle buttons, should derive from javax.swing.JPanel.

And, as always, make sure you use Javadoc-style comments for all of the code you write,

Swing UI Refresher

If you don't remember the details of how to create event-handlers for Swing UI components, the key details are as follows:

Boggle-Buttons

Your Boggle-button display class will display one cell of a Boggle board. Although this is conceptually simple, we want to provide some nice visual cues for the user, so that they know what letters are available to choose from, and so that the board is easy to read. But more on this in a moment.

You are encouraged to name your "Boggle button" class with the same naming convention as all Swing components; starting with a "J". For example, you might call your class "JBoggleButton."

Each Boggle-button needs to track three pieces of state:

  1. The location of the button within the Boggle board. This could be inferred, but it is much easier to implement the Boggle-board features below if the buttons know where they appear within the grid.

    This can be just an (x, y) pair of values, zero-indexed, of course. Allow the location to be specified in the constructor, and provide accessors as well.

    DO NOT expose these values as getX() and getY()!!! Swing components already have accessors with these names for positioning UI components within a Swing container, and it will completely mess up the layout of your UI components. (I spent hours on this bug, so don't make the same mistake!) Instead, call the accessors something else; getBoardX() and getBoardY(), for example.

  2. The value of the Boggle button, as a String. You can use the existing getText() and setText(String) methods of the JButton, or you can provide your own value field and accessor/mutator pair. Just make sure to document your design choice in the Javadocs for your class.
  3. The current state of the Boggle button. This should be one of the following values:

    Represent these state options with a Java enum (called "State" for example), declared as a public member of your button class. Then you can base your code on these state values, and users of your button class can refer to these state values as well.

    Provide an accessor and a mutator for the button's state. The mutator should also update the button's appearance appropriately, based on the state value that was selected. (Details of this are given below.)

Since we want the user-interface to be readable, we don't want to use the "default" button font. So, in the constructor for your class, you should set your button's font to be a large, easily read font. (I chose a 40-point sans-serif font, for example, but you might like serifs.)

Boggle-Button Borders

Your Boggle buttons should indicate their current state (unavailable, available, or selected) based on two visual cues: their "enabled" state, and their border color. Changing the "enabled" state of a Swing component is easy, but JButtons don't have a border by default, so you will get to add one using the setBorder(Border) method.

Add three constants to your button class, UNAVAILABLE_BORDER, AVAILABLE_BORDER, and SELECTED_BORDER. (These should be private constants, since they are only used internally by your class, so declare them as private static final.) Initialize each constant via a call to the appropriate method on javax.swing.BorderFactory to create a line-border three pixels wide.

Once you have these border constants set up, you can make your "set state" mutator update the button's appearance in the following way:

As mentioned before, update your button's appearance within the "set state" mutator. Also, call this mutator from your constructor; don't manually set these values anywhere except from this mutator.

Displaying Boggle Boards

Once you have your Boggle button class ready, you can build out the "Boggle board" class. This class will show an N×N grid of Boggle buttons, allowing users to create words from the letters on the Boggle game-board. Continuing the "Swing UI component" naming convention, you could call this class "JBoggleBoard". The class' constructor should take a single argument N, specifying the dimensions of the boards it will display.

The JBoggleBoard class will of course need a reference to an actual BoggleBoard instance, and the buttons should reflect the contents of the BoggleBoard object.

Displaying the board is not such a difficult problem, especially since you have your Boggle-button class. You can provide a method like this:

    public void setBoard(BoggleBoard b)

The method would just loop through all buttons, setting each button's state to match the BoggleBoard's value for the cell. (You could throw an IllegalArgumentException if the size of the BoggleBoard doesn't match the size of the JBoggleBoard.)

Building Words

Although this class could just let users press any old button, we want to make our display class more intelligent than that. As the user is forming a word, we want to be able to show the user which letters are already part of the word (since each grid-cell can be used only once), and which letters are available for selection. So, manage selection this way:

Keep a list (e.g. an ArrayList) of "selected buttons". Initially the list starts out empty, and buttons get added to this list as the user builds a particular word.

There are several ways to implement this display-update, but an easy way would be to create a separate private method that handles the task of updating button states. It needs to handle two cases:

  1. If there are no selections, iterate through all cells and set them to "available."
  2. Otherwise, do these steps:
    1. Iterate through all cells and set them to "unavailable."
    2. Iterate through the selection-list, and set each of these cells to "selected."
    3. Get the last button in the selection-list, and set all of its immediate neighbors to "available," unless they are already in the "selected" state. (Hint: Being able to retrieve the grid-location of a button will be very useful for this step...)

There is one other situation that the board-display class must handle - if a user clicks a button that has already been selected. In this case, you should truncate the selection-list to end with that button, and then update the button states appropriately. This feature will allow users to "back up" to a certain point and resume creating their word from there.

Methods

Here is a list of the methods that should be provided by your JBoggleButton class:

Testing Your Code

Because it is typically very hard to test user-interface classes, you should just add a simple main() method to your JBoggleBoard class, that throws up a very simple user interface that allows you to poke at the board to make sure it works. It could look something like this:

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              JFrame f = new JFrame("BoggleTest");
              f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

              JBoggleBoard boardGUI = new JBoggleBoard(BoggleBoard.DEFAULT_SIZE);
              f.add(boardGUI);

              f.pack();
              f.setVisible(true);
              f.setSize(300, 300);

              boardGUI.setBoard(new BoggleBoard());
            }
        });
    }

Once you have added code like this to your JBoggleBoard class, you can run it at the command-line, like this:

    java JBoggleBoard

If you have done your job properly, you should see your UI pop up, and you should be able to select words on the Boggle board.

Another one in the bag...

Once you have finished all of your code for this week, leave your work in your ~/cs11/advjava/lab3 directory on the CS cluster.


Extra Credit!

If you want to try a few more challenging things, here are some ideas for you:


Copyright (C) 2008, California Institute of Technology.
Last updated April 23, 2008.