Due date: Monday, April 24, at the beginning of your lecture section.
Bring to lab: Two copies of a design document for classes SimonController.java, Song.java, and SongPlayer.java.
Exercises: 15.2.1 and 15.4.1 (turn in with your HW8 on Monday).
Many of you are probably familiar with the electronic toy named "Simon". Simon is a simple solitaire memory game. The toy is composed of a circular housing with four colored plastic buttons on top. A different musical note is associated with each button. The toy "prompts" the player by playing a sequence of randomly chosen notes. As each note is played, the corresponding button is illuminated. The player must then try to play the same "tune" by depressing the appropriate buttons in the correct order. If the player succeeds, the game plays a new sequence identical to the preceding sequence except that one additional note is added to the end. As long as the player can correctly reproduce the sequence played by the machine, the sequences keep getting longer. Once the player makes a mistake, the machine makes an unpleasant noise and restarts the game with a short sequence.
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 that the player will have to press in an order determined by the computer. We will keep the graphics simple by simply placing the four buttons in a 2 by 2 grid as shown below.
Important note: As with the Frogger lab, you should either use BlueJ's "Run Applet" function to run this program, or, if you want to use the debugger, select "new SimonController()", then right-click on the red object symbol, and select "inherited from Controller" -> "void startController()".
As soon as the buttons are displayed, your program should generate a sequence consisting of a single note/button. It should "play" a sequence by briefly highlighting the buttons that belong to the sequence in order. After a sequence is played, your program should wait while the player tries to repeat the sequence by clicking on the buttons in the appropriate order. If the player repeats the sequence correctly, the program should randomly pick a button to add to the end of the sequence and "test" the player on this new sequence. If the user makes a mistake, the program makes a "razzing" sound and then starts over with a one note sequence. We will explain all you need to know about making sounds in Java 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 that 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. This array is created in SimonController's begin method.
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 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.
For each of the classes you must write, the design should include:
We will grade your assignment both by running your program over the web and by reading a printout of your Java source code. You should submit printouts for SimonController.java, Song.java, and SongPlayer.java. Homework is due by the beginning of your lecture section.
Before submitting your work, make sure that each of the .java files includes a comment containing your name and lab section. Also, 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. Make sure your indentation is all consistent. Refer to the lab style sheet for more information about style.
Print out a copy of your java source files and the Homework 8 Cover Page. This page provides the guidelines for how your homework will be graded. Don't forget to include the assigned exercises. Turn in one stapled hardcopy of all your work, with this cover page on top.
Since BlueJ creates a new HTML file each time you select "Run Applet", you should not modify the file Simon.html. Instead, you can modify Simon2.html to include your name for this homework, but this is optional. The file Simon2.html will display your applet and is accessible on the web outside the lab at http://bj.middlebury.edu/~USERNAME/cs101/hw08_simon/Simon2.html, where USERNAME is replaced by your username.
Back to Computer
Science 101 Home
Department of Computer
Science