iOS For High School Students: Text Adventure Game

This is the next tutorial in the beginning programming for iOS series – for high school students, by high school students! This tutorial will walk you through creating an app from the very beginning. The tutorials are cumulative, so principles will build upon the first iOS For High School Students tutorial. As in the first […] By .

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 5 of this article. Click here to view the first page.

More Framework

You have already successfully enabled the computer to receive input from the user. Although you’re not entirely finished with this task, the next goal is to allow the user to restart easily. When I approached solving this problem, I wrote out explicitly what I wanted the user to experience.

User enters name, proceeds through dialogue, blahblahblah, die, try again,
User enters name, proceeds through dialogue, blahblahblah, die, try again,
User enters name, proceeds through dialogue, blahblahblah, die, try again,
and etc. ad infinitum.

I quickly realized that a while loop would be the tool to run the same statements over and over again.

Select the main.m file. You don’t need the initialization of your game to be repeated, so you want to create the while loop to encompass section #2 while leaving section #1 outside the loop. So replace section #2 with the following:

        // 2 - Set up loop variable
        bool gameIsRunning = true;
        // 3 - Loop
        while (gameIsRunning) {
            // 4 - Show instructions
            [myGame instructions];
        }

Here’s a step-by-step breakdown of the above code:

  • Our while loop will need a way to break out of the loop. You’ll be using a boolean-type variable to make the determination. We define and initialise that variable as gameIsRunning.
  • We set up the while loop to be dependent on the value of gameIsRunning. As long as gameIsRunning is true, the while loop will repeat.
  • We show the game instructions within the loop.

Now you have to create the dialogue to ask the user whether or not they want to restart. If they do want to restart, then you want the game to proceed accordingly and loop through the while loop.

However, if they find the game to be laaamme, then you want them to be able to exit the loop and end the program. This can be accomplished by prompting the user for a response as to how they want to proceed after each call to instructions. To do that, we need a variable to store the response. Add that as follows to section #1 in main.m (right after the line initializing myGame):

        int response;

Now get the user response via the following block of code, which should be added to main.m directly below section #4:

// 5 - Quit option
NSLog(@"\n\nRestart?\n1.Yes\n2.No");
scanf("%i", &response);
if (response==2) {
    break;
}

First, try and understand what the above code means, before reading the summary below.

The function NSLog() provides the user with guidelines on what to enter as a valid response to the question. Then scanf() prompts the user for an integer that will store the value into the variable response. The if statement checks to see if the user answered 2 (which means end this misery). When the user presses 2, the statement break is evaluated, and the program exits the while loop.

Give the program a run to check for accuracy!

As you run the code, you ought to notice that there is currently no way for the dialogue to wait until the user has pressed enter. In the next section, you’ll solve this with the use of a helper function/method.

A Helpful Function

As you might have inferred, you will now be creating a function which will truly be helpful in our program. Although it might not immediately appear useful, just you wait.

Like the instructions method, our function needs a declaration and an implementation. As was true with the instance variable you created above, a function’s declaration location (either inside instructions or the entire game itself) has implications as to where it can be utilized.

To begin, you will declare this function in Game.h, so that the definition can be imported like the Game class. However, note that while the declaration will be in Game.h, the function will actually be independent of the Game class definition (denoted by @interface). Your function will be called waitOnCR (wait on carriage return).

Here is the waitOnCR declaration (a.k.a. its prototype), placed above the Game class definition (above @interface) located in Game.h:

void waitOnCR (void);

The first “void” indicates that the function’s return type value is of type void (basically nothing). You already know the name, and the second void in parentheses is the argument or parameter the function takes (again basically nothing).

And here’s the waitOnCR implementation, placed above the main() function and below the two imports, located in main.m:

void waitOnCR (void) 
{
    while( getchar() != '\n' ) {
    /*flush line buffer*/} ;
    
}

This waitOnCR function is really simple: it has no input or output, just a single task. The implementation might be a little confusing. In order to understand it, you’re going to have to learn a little bit more about input, scanf(), and getchar().

Whenever the user types into the console, the characters are stored in a memory buffer. The function scanf() reads values from that buffer, storing the values to a variable, as directed by the programmer. However, different input functions handle whitespace (spaces) differently.

When scanf() reads input, it only takes input up until it sees a whitespace character. If you were to enter “f f fj sjk”, only the first f would be stored in the variable.

Another function for getting input is called getchar(). It operates by taking the next character from the input buffer and storing it to any variable that is indicated. You will have to consider how both operate in order for waitOnCR() to function as intended.

Every time scanf() is called, an enter is retained in the buffer, because enter must be pressed for the input to be considered. Any data left over in the input buffer gets stored into the variable with the next scanf() call. So, if you typed in “f f” and then pressed enter, only the first “f” would be stored in the specified variable but the second “f” will get stored on the next call to scanf().

Generally, a call to scanf() will wait for input, because the input buffer is waiting for input to be entered. However, any remaining input in the buffer will be taken as an argument to the next scanf() call. This has the result of totally bypassing the second call that should be waiting for the user to enter data. In order to solve this issue, the input buffer needs to be cleaned/emptied.

The while loop in waitOnCR() uses getchar() to check for that leftover input. A summarization of the while loop would be, “While the next character in the input buffer is not an enter, do nothing repeatedly until it is flushed.” By flushing the input buffer, the next call to scanf() will not be bypassed.

If this needs further clarification, feel free to drop a comment below. For now, it’s time to begin to make use of waitOnCR(). As you noticed earlier, the user never gets a chance to press enter after the log: “Your name is ######. Press enter to continue.” All you need to do is update your instructions method to include a call to waitOnCR(), right after section #5.

    // 6 - Wait for CR
    waitOnCR();