COMP 1600 Project 5: Mastermind GUI
40 points (plus extra credit opportunities)
Due: anytime before the final exam (11 am May 13). No solutions accepted after
this time.
You may complete this assignment either individually or with one partner
For this capstone lab project, you will develop a GUI interface for the MMGame
class you developed in the previous project. I will provide a working MMGame
class for you to use and a prototype GUI. The GUI will provide game management and also display a history
of guesses and responses. All user interaction will be through the mouse and all guesses
and responses will use colors instead of digits. When you run the program, the GUI should
look something like this:
As you can see from this figure, the GUI has four basic sections. They are,
from top to bottom,
- Frame Title: This is the title bar area across the top. It is easily configured
using JFrame methods and you have several examples both from handouts and the textbook.
- Menu Bar: The menu bar contains two menus. We did not cover menus
in lecture but the provided code includes a menu bar with one menu that has one item. So you
can see how they are constructed.
- Game History: This is a numbered list of previous moves and responses. It is configured
using nested panels with the outermost panel placed into a scrolling pane. See JScrollPane
on textbook page 569. The figure shows its
initial configuration so each move is shown as a gray box with no response.
- Guess Input: This is the row of four boxes with the button beneath. The four boxes are
actually buttons that have no text. Each time one is clicked on, its background color changes. This
is how the user specifies the sequence of colors for guessing. I have initialized each to the default color white.
The next figures show a game in progress, just before the user presses "Make Guess" for the fourth
guess, and the menu items. To see the real colors, view this document on the web rather than the printout.
How to play the game as shown in these figures
To play the game shown here,
- Run the program or start a new game by selecting "New" from the "Game" menu.
- Click on the four boxes just above the "Make Guess" button to form your guess.
They are initialized to white, which is one of the
colors. Each time you click, the next color in the round-robin
list of colors will fill the background. There is no way to make an invalid guess because only
the allowed number of boxes and colors are shown!
- Click until all four boxes contain your guess then click the
"make guess" button.
- Your guess will be evaluated, then it
and the response will be displayed in the history section. The input boxes also revert to white.
- The response resembles the board version of the game. Each black "o" represents a "complete" and
each white "o" represents a "partial".
- You enter guesses until either you guess the solution or run out of guesses. In either case a
message box will appear with the appropriate message and the "make guess" button is disabled.
Getting Started
Several Java files are provided for this project:
- MasterMind.java, which contains the main()method and
implements the GUI. This is the class in which you will do all your programming. Skeletal solution provided.
- MMGame.class, the class that implements the Mastermind game evaluation.
I have added
another constructor described in the Saving and Opening In-Progress Games
section below. Documentation and bytecode solution
provided.
- MasterMain.java and Master.class, which comprise a substantially complete solution.
Run MasterMain.java to see how the application should work. Provided.
In the I:\COMP\1600\20142-COMP-1600-01\Common folder, you will find a folder called
Project5 containing these files. Copy this into your workspace. If you cannot access the I: drive,
then download the project zip file (right-click) and unzip.
Development Details
Principles you should follow for development
- Work on one component at a time! I will provide guidance below.
- Experiment! The compiler responds within seconds, not hours, so try many variations.
- Have fun! You are creating a masterpiece and developing valuable skills.
1. Start with the basic frame - No Points
- The basic frame is provided. Feel free to resize as needed later on.
- You should be able to compile and run MasterMind.java as provided.
- It will display a titled frame with grayed-out History pane.
- I gave the content pane a FlowLayout
but you are free to use a different layout manager.
- The frame also has a Game menu with the Exit menu item implemented. Try it!
You're off and running!
2. Create additional menus and menu items - 5 Points
- Define all menu-related objects as local variables in the menuSetup() method.
- The menu bar, Game menu and Exit menu item are provided and fully operational.
- Add the additional menu and menu items as illustrated in the diagrams.
- The horizontal separators within a menu are created by calling the addSeparator()
method with the menu (e.g. gameMenu.addSeparator();).
3. Define "stub" action listeners for menu items - 3 Points
An action listener is defined for "Exit". You can follow this pattern to create and add "stub"
action listeners for the menu items you just created. A stub listener just prints an informative
message when activated.
- A stub action listener is one whose actionPerformed() method
only prints a message containing the menu item name to System.out.
- Create a private inner class MenuItemStubListener that implements ActionListener, as a default event handler for remaining
menu items.
- Inside its actionPerformed() method, call e.getActionCommand(),
where e is the parameter, to
retrieve a String containing the menu item text for the selected menu item. Then display this in a message
using JOptionPane.showMessageDialog(). Here's an example, where e.getActionCommand() returns "Close":

- Back in menuSetup(), create a MenuItemStubListener object and add it to each
of the menu items. The same action listener object can be added to all the menu items.
STOP! Test all the above to make sure it works before proceeding. Each each menu item
should be connected to the action listener and display an appropriate message when selected.
4. GUI objects for obtaining user guess - 24 Points (allocated as indicated)
4.1 (4 Points) The guess input technique shown in the figure is straightforward
to program. Here's what you need to do.
- Declare two instance variables:
-
private JButton[] inputButtons;
for the array of buttons that will be clicked to specify the guess, and
- private JButton makeGuess;
for the "Make Guess" button. Remember:
instance variables are declared outside any method, in this case they are
listed at the top of the class.
- Define two action listener classes:
- MakeGuessListener, for the "Make Guess" button.
- InputButtonListener, for the array of input buttons.
- Write "stub" actionPerformed() methods for both of those. They
should respond in some way -- a JOptionPane message or
System.out.println() message.
STOP! Do not proceed until everything compiles correctly!
4.2 (6 Points) The next 6 steps describe code in the provided private method,
private JPanel inputSetup().
- Note that I have declared and created a JPanel object called inputButtonPanel to hold all the buttons.
- Create the inputButtons array to have NUM_PEGS JButton elements.
NUM_PEGS is provided and is currently set to 4.
- Compose a loop to create the NUM_PEGS JButton objects. In each iteration of this loop,
a JButton object will be created and assigned to an element of the inputButtons array. The
button will
have no label. Add an InputButtonListener object to the button (all buttons can share
the same listener object, so create it above the loop). Set its background to
colors[DEFAULT_COLORS_INDEX]. The array colors is provided.
- Create and assign the "Make Guess" JButton. Add a MakeGuessListener to it.
- I used the JPanel default layout manager FlowLayout.
- use the setPreferredSize() method to specify
the dimensions of inputButtonPanel and all the buttons it contains.
- setPreferredSize() requires a Dimension parameter. A Dimension object
simply contains int width and height. For example, if you had a button named press you could
set its size to 100 x 25 pixels using Dimension buttonDim = new Dimension(100,25);
press.setPreferredSize(buttonDim);
- Add all the buttons to inputButtonPanel.
STOP! Test all the above to make sure it works so far before proceeding! The input buttons should be white,
and the two stub action listeners should respond with whatever messages you have programmed into them.
4.3 (6 Points) This is a good time to fill in the actionPerformed()
for the InputButtonListener class. Here's what it needs to do:
- Get a reference to the button that was clicked. actionPerformed() has an ActionEvent parameter and you can use its getSource() to
do this. See textbook page 248. Note: you will need to use type-casting to assign the return
value to a JButton variable.
- Get the button's background color using getBackground().
- Determine which index in the colors array contains this background color. I strongly recommend you write a private method
that has one parameter to receive the background color, and returns the array index for that color. Its algorithm will require a small loop to find the match.
This method will also be needed below.
- Change the button's background color, using setBackground(), to the next
color in the array. Don't forget to wrap around when you get to array index
NUM_COLORS! The '%' operator is good for this.
STOP! Test all the above to make sure it works so far before proceeding! Each of the input buttons should independently cycle through the
array of colors. It should only cycle through the first NUM_COLORS elements of the array.
4.4 (8 Points) Finally, write actionPerformed() for the MakeGuessListener
class. Take it in phases:
- Start by
transforming the state of the input buttons into a suitable string for the MMGame's
guess() method. Do that by composing a loop to traverse the inputButtons
array. In each loop iteration:
- get the button's background
color and determine its array index in the colors array. Hey, didn't you
just do the same thing above? If you defined it as a private method as I recommended, you can just call that method here.
- Convert this array index to a string and concatenate it to the end of your
guess string.
- After completing the loop, you should have a string containing the guess in a format
that the MMGame class understands. Now you can interact with MMGame and produce
some test output. The provided code includes an MMGame object called game.
You can use game.guess() to register the guess. Then for testing, you can use
System.out.println() to display the values of game.getSolution(), game.numComplete(),
and game.numPartial(). Test everything thoroughly until you are convinced
it works correctly.
- Add the logic and interaction for finishing a game. Call game.isGameOver() and if it returns
true, then show a congratulatory JOptionPane.showMessageDialog() to the user!
If game.isGameOver() returns
false and game.getNumGuesses() returns MAX_GUESSES then show
a conciliatory JOptionPane.showMessageDialog() to the user.
In either of these situations, disable the "Make Guess" button using its setEnabled() method with argument false.
- Reset the input button colors to all white after each move is processed.
STOP! Test all the above to make sure it works properly before proceeding!
This is a major milestone! Pat yourself on the back and get ready for more.
5. GUI objects for displaying response and game history - No Points
These are provided, so I will simply describe them here. At this point you have done some major GUI programming
and this part does not introduce any new concepts other than 2-D arrays. As shown in the figures, I have programmed the GUI to display each guess in a numbered row, with
the game's response to the right of the guess.
I defined a private JPanel historySetup() method that returns a JPanel
containing all the rows. Back in the constructor, I called historySetup()
then created a JScrollPane using the returned JPanel as its constructor
argument. This is what gave you the ability to scroll through the history rather
than display it entirely.
So there is a JPanel to contain the entire grid. Within this JPanel, I added
a separate JPanel for each row. And within each row's JPanel, I added:
- a JLabel containing the row number
- a set of NUM_PEGS disabled JButtons representing the guess
- a set of NUM_PEGS JLabels representing the response
The easiest way to program this is to create all the JButtons and JLabels with
a default background and foreground color and place them into a hierarchical structure
of JPanels. Thus they are displayed all the time - see rows 4 through 7 in the illustration above.
When a guess is made and evaluated,
all you have to do is change the colors of JButtons and JLabels in the appropriate
row!
Each row number is simply a JLabel.
For the guess history, I chose to use disabled JButtons having the same size as the input buttons.
This contributes to a simple and consistent look. They are initially drawn using a gray background
then as each guess is registered the colors in the appropriate row are changed to match those of the input buttons.
For the response history, I chose to use JLabels. Each JLabel has the value
"o". I then manipulate its visibility and meaning by setting the foreground
color (e.g. the color used to render the text). To make it invisible at the
start of a new game, I set its foreground color the same as its background color.
To display a "complete" match, I set its foreground color to black. To display
a "partial" match, I set its foreground color to white.
Again, I used the default FlowLayout manager for
all the JPanels, in conjunction with setPreferredSize() to make everything line up pretty.
Another issue is data structures. Since the guess JButtons and the response
JLabels will have their colors manipulated by action listeners:
- I declared the guess JButtons as a 2-D array instance variable, e.g. private
JButton[][] history;
- I declared the response JLabels as a 2-D array instance variable, e.g.
private JLabel[][] response;
- The first index represents guess number (row) and second represents position (column).
- The arrays and objects were created as needed in historySetup()
6. Moving a Guess into the History - 5 points
Program the "Make Guess" action listener to record each guess and the game's response into
the history by manipulating the colors of the JButtons and JLabels in the appropriate row of the history and response
arrays. These are described in Section 5 above, so read it now if you haven't yet done so. You'll know what row to use by retrieving the number of guesses from
the provided MMGame object called game.
For each button in the selected row of history,
set the background color to the background color of the corresponding guess input button. For labels in
the selected row of response, set the foreground color to Color.black for each
of game.numComplete() and set it to Color.white for each of game.numPartial(). In
other words, if the number complete is 1 and number partial is 2, set one of them to black, two to white,
and leave the fourth one alone.
7. Reset the History when Game Ends - 3 Points
Section 4.4 above, described what to do when a game is over (due to correct guess or out of guesses). In particular,
the program is to display a message dialog. For this section, you are to extend your program to
visually reset the history and response elements after the user responds from the message dialog. For all
the history buttons, set their background color to Color.gray. For all the response labels,
set their foreground color to be the same as their background color.
8. Minor menu items - Extra Credit, Up to 5 Points
If you are ready for something not so taxing, implement a few menu item action listeners. The
easy ones are:
- New : should reset the history to its initial visual state (see item 7), and create a new MMGame object.
- Close : For now, disable the "Make Guess" button and reset the history and input buttons
to initial visual state.
- View Solution : Call game.getSolution() to get the solution
then display it. If you use JOptionPane.showMessageDialog(), you might have a tough
time displaying the solution visually by color rather than text names.
- MasterMind Rules : Use JOptionPane.showMessageDialog() to
describe how to play the game. This will require multiple lines both in your
program and in the resulting message dialog. A string cannot be continued
onto multiple lines of your program, so patch together your string using the
'+' string concatenation operator at the end of each line. To get multiple
lines in the message dialog, embed an '\n' (newline) character in
your string.
- About... : Use JOptionPane.showMessageDialog() to relay a brief message with your
name and a 2014 copyright. Also state that Mastermind is a registered trademark of Pressman Toy Corporation.
The remaining menu items relate to file input and output.
9. Saving and Opening in-progress games -- Extra Credit, Up To 10 Points
It should be possible to save an in-progress game to disk and later open it and continue playing. Swing provides
a very convenient class called JFileChooser to facilitate filename specification for saving or
opening files. This is covered in textbook section 10.8 starting on page 522.
Dialogs for selecting file names
Implement the "Open" menu option by creating a JFileChooser object, invoking its showOpenDialog()
method then accessing the filename (if the action was not canceled) using its getSelectedFile() method.
Implement the "Save As" menu option similarly except use showSaveDialog() instead of showOpenDialog.
Implement the "Save" menu option by first checking to see if there is a "current" file name (a name under which
it has already been "Open"-ed, or "Save As"-ed since the last New) -- use a String variable for this.
If there is, use it. If not, treat this as a "Save As" as described above. This is good incentive to
use the same action listener for both "Save" and "Save As".
Optional feature: If you have time, consider the following. For "New" and "Open",
maintain and test a variable that indicates whether there is a current game that has not
been saved since the last move. If there is, give the user an opportunity to save it before doing the New or Open.
Reading and writing game states
Once you have the filename, you need to open the file (create a File object) then perform the
read or write operations to handle the "Open" or "Save" function. It will be sufficient to save the game
state as all integers, so you can use the DataInputStream and DataOutputStream
classes for input and output, respectively.
Here are the values that need to be stored in the file. You may add to this list if necessary for
your particular implementation.
- Number of positions that need to be guessed *
- Number of different colors that each may take *
- Maximum number of guesses permitted *
- The solution
- Number of guesses made so far
- The guess and response for each guess
* Include these even if the user is not able to
change them via menu selection (this ability is not part of the assignment), in which case they can
be discarded once read.
I suggest you store the guess as an integer (i.e. the index of the colors array) sequence.
Changes needed to MMGame class
The ability to open and resume a saved game means the GUI client has to have
the ability to set both the solution and the number of guesses made so far,
in the MMGame object. I have added another constructor to the MMGame
class to permit this.
public MMGame(int n, int k, String solution, int guesses)
- n is length of solution,
- k is number of digit values,
- solution is
the specified solution, and
- guesses is the number of guesses made so far.
You should use this to create a new game whenever the user elects to resume a saved game through the
"Open" menu item.
The Partner Option
You have the option of working with a partner to develop one solution. If you do this, you will
follow the "pairs programming" technique. The first decision will be whose login account to use since you
can only use one. Once that is decided, then pairs programming is pretty straightforward: both of you work at one PC
(you can use a second one for Googling and API lookups). The "driver" is at the keyboard typing in code and testing.
The "navigator" is at the side, directing the driver, giving advice, and watching for keystroke errors. After 45 minutes
you must switch roles.
To Turn In
When finished, copy MasterMind.java
into your I:\COMP\1600\20142-COMP-1600-01\DropBox folder. If you are working with a partner, submit to only one partner's
folder. Be sure the comments in the course code file has both names.
Point Allocations
Points | Item |
5 |
2. Add remaining menu and menu items |
3 |
3. Write stub action listeners for menu items |
4 |
4.1. Define JButtons for input and make guess, write stub action listeners |
6 |
4.2. Complete the private inputSetup() method |
6 |
4.3. Complete the actionPerformed() method for InputButtonListener |
8 |
4.4. Complete the actionPerformed() method for MakeGuessListener |
5 |
6. Move a guess into the history |
3 |
7. Rest the history when a game ends |
5 |
Bonus: Implement menu items New, Close, View Solution, MasterMind Rules, About |
10 |
Bonus: Save and Open in-progress games |
[ COMP 1600
| COMP 1600 Projects
| Peter Sanderson
| Math Sciences home page
| Otterbein ]
Last updated:
Peter Sanderson (PSanderson@otterbein.edu)