1

I'm working on a text based game in C++. I'm wondering how would you go into different events without nesting so many if statements. An example of what i'm talking about is...

if ( userChoice == 1 )
{
     //do something
     cout << "Pick 1 , 2, or 3" << endl;
     if ( userChoice == 1 )
     {
           cout << "pick 1, 2, 3/ some storry line" << endl;
           if ( userChoice == 3 )
           {
                cout << " storyline...123...." << endl;
                if ( userChoice == 2 )
                {
                       //etc
                }
           }
     }
}

How would I do this concept without branching the if statements?

Raptor
  • 53,206
  • 45
  • 230
  • 366
freemann098
  • 284
  • 1
  • 7
  • 22
  • Well for one why would you possibly need `(userChoice == 1)` twice, second look into the switch statement – aaronman Jul 15 '13 at 02:19
  • Organize logically separate logic and actions into different functions. Of course you'll need conditionals inside them, but you'll immediately see the advantage of doing so once you've tried it. – Mark Garcia Jul 15 '13 at 02:20
  • You might also look into TADS and Inform, which are languages to facilitate coding such games. – Mawg says reinstate Monica Apr 11 '19 at 09:42

4 Answers4

5

You can get rid of the branching if statements by using a data driven design.

At the base you have a loop that checks for input and using the current context evaluates the input and then changes context.

Each of those if statements becomes a 'scene' (or room or state, choose a name that has meaning for the program you are writing). Each scene has an id, a description, and a set of valid inputs and scene numbers that the result is.

You have a scene that represents the end.

You have a loop that goes:

loop until current scene is the end
 Print current scene description
 Ask for input
 Evaluate input

Evaluate input checks the input against the valid input for the current scene and if valid sets current scene to the specified scene.

The program initialises the current scene to the first scene and then starts the loop.

So for your example (obviously incomplete, but should give you the idea of the data you'd need) you would have the scenes of:

id: 1
description: "Pick 1 , 2, or 3"
inputs: "1" => 2, "2" =>, "3" =>

id: 2
description: "pick 1, 2, 3/ some storry line"
inputs: "1" =>, "2" =>, "3" => 3

id: 3
description: " storyline...123...."
inputs: "1" =>, "2" =>, "3" =>

Usually the data would come from files.

Here is an example (This hasn't been compiled or debugged):

struct Scene
{
    Scene(int id_, int description_)
        : id(id_)
        , description(description_)
    {
    }

    int id;
    std::string description;
    std::map<std::string, int> inputToNextScene;
 };

void main(int, char **) 
{
    std::map<int, Scene> scenes;

    int ids = [1,2,3];
    std::string descriptions = ["first", "second", "third"];
    int nextScenes [3][3] = [ [1, 2, 3], [1, 3, 2], [1, 2, 0]];
    std::string input[3] = ["1", "2", "3"];

        for (int i = 0; i != 3; ++i)
        {
            scenes[ ids[i] ] = Scene(ids[i], descriptions);

            Scene& scene = scenes.at(ids[i]);

            for (int j = 0; j != 3; ++j)
            {
                scene.inputToNextScene[ input[j] ] = nextScenes[i][j];
            }
    }

    int currentScene = 1;

    std::string input;

    while (currentScene != 0)
    {
         Scene& scene = scenes.at(currentScene);

         std::cout << scene.description;
         //Maybe put in a prompt and check currentscene against previous before      printing description
         std::cin >> input;
         if (scene.inputToNextScene.find(input) != scene.inputToNextScene.end())
         {
             currentScene = scene.inputToNextScene.at(input);
         }
    }

    cout << "The end!";
}
Dominique McDonnell
  • 2,510
  • 16
  • 25
1

See this question and my answer.

Short version:

Use separate function or object for each "branch". Instead of "if/else", use std::map. Map user input ids to function that handle them.

Community
  • 1
  • 1
SigTerm
  • 26,089
  • 6
  • 66
  • 115
0

I'd make a separate function for each option:

void doStoryLine1() {
    cout << "Options: ..." << endl;
    if(userChoice == 1)
        fightDragon();
    else if(userChoice == 2)
        fightOrcs();
    else if(userChoice == 3)
        marryPrincess();
    else
        /* invalid option? */
}

void doStoryLine2() {
    ...
}

void selectStoryLine() {
    cout << "Options: ..." << endl;
    if(userChoice == 1)
        doStoryLine1();
    else if(userChoice == 2)
        doStoryLine2();
    else if(userChoice == 3)
        doStoryLine3();
    else
        /* invalid option? */
}
nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • I could but this would be doing the same thing, but with functions because withing the functions it would either branch in if statements again or I would have to make many functions. – freemann098 Jul 15 '13 at 02:26
  • You're better off making many functions. You are more likely to make your code nice and modular, and make it easier to maintain. – nneonneo Jul 15 '13 at 02:27
-2

The problem you described, appears to be a common problem in Asynchronous programming (e.g. Node JS).

You can break out of these loops by using control-flow programming, such as sequences / waterfalls and promises.

Note: I have not used this header before, but it should work similar to how one does futures in JS: Futures vs. Promises

Code in JS (You get the idea in C++)

/** Sequence **/
new Sequence()


.then(function(next, arg1, arg2) {
    // stage 1...
    next(arg1, arg2);
})


.then(function(next) {
    // stage 2...
    next();
});



/** Promise **/
var a = Promise();

function stage1() {
    // capture input 1...

    a.fulfill(input1);

}();

a.when(function(err, data) {
    console.log(data);

});
Community
  • 1
  • 1
jhtong
  • 1,529
  • 4
  • 23
  • 34
  • -1 Futures are a horrible choice for addressing this problem in C++. Also, C++ futures do not have `.then` (yet), which renders the underlying idea mostly unusable. – ComicSansMS Jul 15 '13 at 08:43
  • Perhaps you could elaborate further? IMO this does not deserve a downvote! – jhtong Jul 15 '13 at 09:47
  • Futures in C++ do not allow dispatch. Instead you can think of them as one-time-channels for passing a value from one thread to another. In the absence of multiple threads, you could even replace all `future`/`promise` pairs by a variable of type `T` (as long as you don't care for the only-one-write-allowed property). A `.then` style-dispatching as suggested can be implemented in C++, but future is the wrong tool for the job here- especially since future's primary design goal is to act as a multithreading primitive. – ComicSansMS Jul 15 '13 at 12:19