Assignment 1: Simon
For this lab you'll be fleshing out the Simon game we started together in lab/lecture. In doing so, you should:
- Familiarize yourself with the Xcode IDE,
- Get comfortable with the Objective-C programming language,
- Recognize some of the key software design patterns used in iOS development (including target-action, delegation, and MVC)
Good news: if you've been following along in class and during our lab sessions you're at least halfway done with this assignment (including extras)!
At this point you should have already figured out how to clone the read-only course repository (at http://bitbucket.org/michaelee/cs442) and subsequently add, commit and push to our private, shared repository on BitBucket (named
cs442-spring12-yourusername.git). Your only "submission" will be performed by pushing to this shared private repository.
Before proceeding -- even if you've already put some work into the Simon game previously -- make sure you can do the following:
- Identify the model, view and controller components of your application, and what their responsibilities are.
- Explain how the user interface components are associated with the various properties of the ViewController class (the
- Explain how the target-action association is made from events that occur in the UI (e.g., button taps) to methods (typically marked
IBAction) in your view controller object.
- Set breakpoints in your view controller, run till stopped, then step through and examine the state of the application. Make sure you can locate:
- the current thread's stack trace
- local variables
- instance variables (for these last two, remember that you can either hover over symbols in the editor or look in the debug window for a listing)
- Log data with
NSLog(and find the console output).
- Explain why GUI-updates made in event handling functions (e.g., those marked
IBAction) are not "seen" until after the functions return.
- Run your project through one of the profiling tools (e.g., the Leaks tool) and explain the output.
The following paragraphs describe the functional requirements of your final application.
Note that each builds on the one before, and as such there may be some redundancy. If you wish you may certainly read through the entire section and skip ahead to implementing the "final" version of the app from the outset. If you're not entirely comfortable with iOS development or Xcode just yet, however, I recommend that you proceed stepwise.
¶ Basic gameplay
Your app should implement a basic Simon Says game, where the different colored buttons from the original game are replaced with labeled buttons. A "start" button should be incorporated into the interface, which will start playback of the sequence. (If you'd like to see a classic implementation of the original game, see http://www.lilgames.com/simon.shtml.
After playback -- during which all interface elements should be disabled (see UIView's
userInteractionEnabled property for affecting this) -- the user will enter her guess from memory and:
- if the entered sequence matches, playback will commence again with the same sequence, except with one additional step
- if the sequence is incorrect, playback will restart with just the first step of a new randomly generated different sequence
At any point during entry, the player may tap the "start" button again to begin a new game (with a different sequence).
- The sequence should be randomly generated and stored within the app's model object (SimonEngine).
- During playback, each button in the sequence should be flashed separately --- if a button is to be flashed more than once in succession, there should be a period between flashes where the button is not highlighted.
¶ Win & Loss
After a sequence of predetermined length (initially hardcoded) has been correctly entered from memory, the player wins, and the game should display a suitable label. Tapping the "start" button will hide the label and start a new game.
If and when the player taps a wrong button while entering the sequence from memory, the game should display a suitable "Game Over" label, while at the same time highlighting the button she should've entered. Note that the button should remain highlighted until the "start" button is tapped, at which point the label will be hidden and the button unhighlighted to commence a new game.
¶ "Reset" (i.e., the cheat button)
Next, you should incorporate a "reset" button into the game view which allows the player to, in essence, cheat. Tapping "reset" will play back the current sequence again (up to the same point) so as to allow the player to refresh her memory.
After playback has been completed the second time, the user will once again be able to enter her guess. If successful, the game advances (or she wins, if it is the last step), and if unsuccessful a new game is started, as before.
Important: the "reset" button can only be used once per playback, and only before starting to enter a sequence from memory! E.g., consider the following scenario:
- Player has correctly entered a sequence of length 5
- The game plays back the same sequence, with an added 6th step
- Player failed to remember the sequence, and hits "reset"
- The game plays back the same sequence (including the 6th step)
At this point the "reset" button is no longer useable. The player must either guess or hit "start" to play a new game. If she correctly enters the current sequence, she may use the "reset" button again after the next playback sequence.
¶ Splash screen
Create a separate view (controlled by a separate view controller) that appears when the app is first started. This view should contain a logo of sorts (could be just a large text label) and a "Play game" button that results in the game view you've been working on previously to be displayed.
You should also add a button to the game view that, when tapped, will take you back to the splash screen.
Note that transitioning between the splash screen and the game screen should not affect the current game (if one is already underway). E.g., the following scenario is possible:
- Player starts app and taps "Play game" to go to the game screen
- Player plays up to a playback sequence of length 4
- Player taps button to return to splash screen
- Player taps "Play game" button to go back to game
- Player taps "Reset" to see sequence that was previously underway
For this assignment, please stick to using the view controller's
presentViewController: animated:completion: method (i.e, for presenting a modal view) instead of a navigation controller --- we'll save the latter for the next assignment.
¶ Difficulty level
Incorporate a slider into the splash screen that allows the user to select a difficultly level: the higher the difficulty, the longer the sequence the player must correctly enter to win. Note that dragging the slider should dynamically update a label that indicates the current difficulty level (see below for a possible implementation):
Note that when returning to the splash screen from an ongoing game, the difficulty setting should reflect the current level. If the difficulty setting is modified when returning to the splash screen from an ongoing game, tapping "Play game" should end the current game. If the difficulty setting is not modified, however, the current game can be resumed (as previously described).
To accompany the different difficulty levels, an indicator (label) should also be placed in the game view that shows the player the length of the last playback sequence, and the length of the final playback sequence. E.g., if the difficulty setting is 15 and the player is currently entering her guess for a playback of length 8, then the indicator should display "8/15".
There are myriad ways of implementing the different pieces of this assignment, and for the most part you are at liberty to choose your own methodology. There are, however, a few details that I ask you to pay particular attention to:
You should enforce a clean separation of model, view, and controller logic. Because we have yet to discuss how to create custom views, this really just amounts to separating the first two. Keep in mind that your model object(s) should encapsulate all data and logic that is not directly related to controlling on-screen elements or dealing with user input.
While the iOS APIs more or less enforce a single view controller to top-level view mapping, it is still possible to tightly couple your view controllers. Don't! It is considered bad style to directly invoke many methods of one custom view controller from another.
A parent view controller may need to initialize some properties of its child view controller, but a child controller has very little excuse to invoke methods of its parent (or those of other unrelated controllers and objects). It might help to think of a view controller and its view as a "drag-and-drop", reusable software component. If you couple it too tightly with other application components, it's highly unlikely that you'd be able to reuse it in a different setting!
Avoid the use of global variables. Instead, use declared properties to create instance variables for long-lived data.
Do not use manual reference counting. Use automated reference counting (ARC)!
Try to do as much UI configuration as possible in the nib file (i.e., using the interface builder component of Xcode). Programmatic configuration should only be done when absolutely necessary.
Pay close attention to your use of the Objective-C language and Foundation framework -- it'll pay off in spades later. Some pointers:
- Whenever possible, use the dot syntax for accessing properties instead of invoking accessor methods directly.
- Prefer Foundation collection classes (e.g., NSArray, NSDictionary and their mutable versions) to rolling your own data structures or using C APIs.
- Think hard about where properties need to be initialized in your classes. Often times the
initmethod is the correct place (in which case place initialization code within the
if (self = [super init])guarded block), but in controllers sometimes you need to wait until
In this and all future assignments, this section provides you with a list of "extra" features you can implement to increase the likelihood of receiving a "check-plus" grade.
- Jazz up your UI! You can use colors and/or images for the buttons to really recreate the original "Simon" experience! ;-) More kudos for more polish.
- Add yet another difficulty setting that adjusts the speed of playback (i.e., length of button highlights during playback).
- Add another splash screen button that takes you to a two-player mode, where the first player is able to "record" a sequence for the other person to guess, and the second player wins by guessing the sequence. Note that the "Reset" button should still be available, and the length of the sequence should be controlled by the difficulty setting described above.
Of course, feel free to implement other extra features you think of -- you may want to run ideas by me first so I can give you an idea of how worthwhile I think they are.
Be sure you list all the extras you implemented in your app in a comment atop your app delegate's header file (i.e.,
AppDelegate.h), so I don't miss out on anything cool.