CS 51 Homework Laboratory # 8
Simon
Objective: To gain experience working with arrays.
For this laboratory exercise, we would like you to write a Java program to allow one to play a simple game like "Simon". Like the original, our game will involve four buttons which the player will have to press in an order determined by the computer. Given the limitations of Java's layout managers, we will keep the graphics simple by simply placing the four buttons in a 2 by 2 grid as shown below.
Your program will consist of five main classes:
The good news is that we will provide complete implementations of the first two classes as part of the starter folder for this lab. The details of how to use our code are described in the following section.
To complete this lab, you will need to work with our ButtonPanel and NoisyButton classes and one new feature of the Java system, support for manipulating audio files.
Working with audio in Java is quite simple. There is a method named "getAudio" associated with the "Controller" class you extend when defining your main class. Like "getImage", this method expects a string that names a file as a parameter. This file must be in a standard format that represents audio segments. If it is, the system will read the file and return an object of the class AudioClip. We will include five audio files in the starter folder for this lab. The files "tone.0.au", "tone.1.au", "tone.2.au" and "tone.3.au" describe the sounds the NoisyButtons should make. The file "razz.au" contains the unpleasant noise your program should make when the user goofs.
There is only one method of the AudioClip class you will use in this lab. The method is named play. It expects no parameters and simply plays a sound. So, if you declare a variable as:
AudioClip nastyNoise;
and in your Controller you assign it a value using:
nastyNoise = getAudio("razz.au");
then you can say:
nastyNoise.play();
when you want to make a funny sound.
Our class NoisyButton produces buttons that look and act like those found on a Simon game. These buttons know how to beep and flash.
Like other GUI components, a NoisyButton needs to have some other object that "listens" for events that involve the button. The object you choose to use as a listener for your game's buttons must implement an interface we have designed named NoisyButtonListener. We have included the definition of this interface in the starter file for this lab. The interface definition is:
public interface NoisyButtonListener { // Method invoked when a NoisyButton is clicked public void noisyButtonClicked(NoisyButton source); }
That is, to listen for NoisyButton events, an object must provide a noisyButtonClicked method. We expect you to use your SimonController as the listener, so you should define a noisyButtonClicked method in that class. When this method is invoked, the NoisyButton that has been clicked will be passed as a parameter.
The NoisyButton class provides one method which you will use in your program. The method is named "flash()". It makes the button flash and plays the sound associated with the button.
The other class we will provide is called ButtonPanel. It creates the collection of NoisyButtons that form the game board. The ButtonPanel class is also a GUI component. So, after you create a ButtonPanel, you may add it to your program's display.
The ButtonPanel class provides two methods. The first method is used to assign a listener to all of the buttons in the panel. It is named addListener and expects an object that implements the NoisyButtonListener interface as a parameter. The other method will be used when you need to add a button to your "song". It is named getRandomButton and it simply returns a randomly chosen button from the panel.
The ButtonPanel constructor requires that you pass it the AudioClips for the sounds the buttons will make. Rather than expecting four separate parameters, the class is defined to expect as the only parameter to its constructor one array of AudioClips that refers to the four button sounds. You will have to create this array in SimonController's begin method.
To complete this program, you will need to construct a class that extends Controller that will act as your "main program" and two classes that will manipulate the "song" played by the game.
Your Song class will manage the sequence of tones corresponding to the "song" played by the game. Internally, this class will represent the song using an array of NoisyButtons. The actual number of notes in the song will vary depending on just how good the player is at the game. So, your Song class will need to construct an array big enough to hold a sequence longer than any player is likely to remember (100 is certainly safe!) and then use a separate int variable to keep track of how many notes are currently in the sequence. You will also need to use another int variable to keep track of which note the user is expected to play next. For example, suppose there are currently 8 notes in the song, but the user has not yet guessed any notes. The class Song will need to keep track that only 8 of the possible 100 notes are in the current song, and it will need to keep track that it is waiting for the user to play the first note. If the user gets the first note right, then it will need to remember that it is now waiting for the second note, etc.
The Song class must provide methods to play the song, to determine the next button the player is expected to click, to add a note to the song and several others. Determining exactly what methods are appropriate to include in Song and what parameters they should expect will be an important part of the work you should do to prepare for this lab.
In addition to the Song class, you will need a separate SongPlayer class to assist the Song class when it is told to play the song. This may be surprising because playing the song is fairly simple for the most part. The song is represented by an array of NoisyButtons. Each NoisyButton knows how to play itself (i.e. each will respond to the invocation of its play method). The problem is that in order to play the song correctly, you will need to pause between the individual notes (and it will be best if you also pause for a second or so before beginning to play the song). The pause method can only be used within an ActiveObject. The SongPlayer class will be a class that extends ActiveObject. Whenever the Song class is asked to play itself, it will create a SongPlayer to actually do the work. The array that holds the song and the song's current length will be passed as parameters to the SongPlayer constructor. The run method of the SongPlayer will simply play all the notes (with appropriate pauses) and then terminate.
Finally, you will need to define a SimonController class. The constructor for this class will create a ButtonPanel and a Song. In addition, the class should include a noisyButtonClicked method so that it can be used as the listener that determines how to react when the player clicks on a button. We provide the code to load the audio clips into an array of sounds.
The noisyButtonClicked method is only called after the user clicks on a button. You can determine which button was clicked on by by examining the button that is passed as a parameter to the method. What happens next depends on whether or not the user clicked on the button corresponding to the next note in the song. If not, the program should make a nasty noise and start a new game (by creating the first note and playing it).
If the user got it right, there are two possibilities. The first is that it was the last note of the song. If so, add a new note and play the entire song to the user so they can start over with emulating the notes. If instead there are more notes to play, the program should keep track that the user is ready to play the next note, but then do nothing more. Of course the noisyButtonClicked method will be executed again when the user clicks the next button.
In summary, the noisyButtonClicked method begins execution when the user clicks on a button, and terminates when it needs to wait for the user to click a button again. The work it does in that method depends on whether or not the user's guess was correct, and, if so, whether the user still has more notes to repeat or whether she has finished all the notes in the song so far.
This week we will again require that you prepare a written "design" for your program before lab. At the beginning of the lab, the instructor will briefly examine each of your designs to make sure you are on the right track. At the same time, the instructor will assign a grade to your design. Since you now should have enough experience to prepare a good design, this week's design will count as 20% of your total grade for this lab.
For each of the classes you must write, the design should include:
As usual, we suggest a staged approach to the implementation of this program. This allows you to identify and deal with logical errors quickly. The size of your applet should be 250 pixels wide by 250 pixels tall.
The lab is due at 11 PM on Wednesday as usual. When your work is complete you should deposit it in the appropriate dropoff folder a folder that contains your program and all of the usual files needed by Eclipse. Make sure the folder name includes your name and the phrase "Lab 8". Also make sure that your name is included in the comment at the top of each Java source file.
Before turning in your work, be sure to double check both its logical organization and your style of presentation. Make your code as clear as possible and include appropriate comments describing major sections of code and declarations.
public class ButtonPanel extends JPanel { // Construct button panel. // sounds is an array containing the tones played when the buttons flash public ButtonPanel(AudioClip[] sounds); // add a listener to all four buttons public void addNoisyButtonListener(NoisyButtonListener listener); // return a random button from the panel public NoisyButton getRandomButton(); }
public class NoisyButton extends JPanel implements MouseListener { // construct a new NoisyButton // noise determines the tone played when the button is clicked // shade determines the unhighlighted color of the button public NoisyButton(AudioClip noise, Color shade); // Assign an object to listen for when someone clicks on the button public void addListener(NoisyButtonListener listener); // Make the button flash by creating an ActiveObject that does the work public void flash(); } public class AudioClip { public void play(); }
We will provide you with the following start-up file to help you get going with the SimonController class:
import objectdraw.*; import java.applet.AudioClip; // Name: // Lab: public class SimonController extends Controller implements NoisyButtonListener { private static final int NUM_SOUNDS = 4; // the number of distinct sounds // corresponding to the game buttons private AudioClip nastyNoise; // a razzing noise private Song theSong; // a sequence of buttons/tones that // the user must reproduce // create the display of four buttons on the screen and // associate appropriate noises with those buttons public void begin() { // load the nasty noise nastyNoise = getAudio("razz.au"); // create the array of audio clips for the buttons AudioClip[] buttonSounds = new AudioClip[NUM_SOUNDS]; for (int i = 0; i < NUM_SOUNDS; i++) { buttonSounds[i] = getAudio("tone."+i+".au"); } // create the button panel ButtonPanel theButtons = new ButtonPanel(buttonSounds); // add a listener for user clicks on the buttons theButtons.addNoisyButtonListener(this); // add the panel of buttons to the window getContentPane().add(theButtons); validate(); // add code to start a new song and play it for the user } public void noisyButtonClicked(NoisyButton theButton) { // Uncomment and use this if/else statement //if () if the expected button was clicked //{ // worry about whether it was the end of the song or not //} //else the player made a mistake //{ //} } }
Grading Point AllocationsValue | Feature |
Design preparation (4 points total) | |
1 point | instance variables & constants |
1 point | constructors |
1 point | methods |
1 point | noisyButtonClicked method |
Readability (5 points total) | |
2 points | Descriptive comments |
1 points | Good names |
1 points | Good use of constants |
1 point | Appropriate formatting |
Code Quality (7 points total) | |
1 point | Conditionals and loops |
2 points | General correctness/design/efficiency issues |
1 point | Parameters, variables, and scoping |
2 points | Good correct use of arrays |
1 point | Miscellaneous |
Correctness (4 points total) | |
1 point | Playing songs |
1 point | Comparing user input with songs |
1 point | Restarting correctly when user makes mistake |
1 point | Lengthening song correctly when user is right |
051