0

I'm teaching my self C++ on the side and i realize this question may seem remedial to some. In the game I'm making as part of the learning process I want the user to be able to pick a difficulty and when they pick one or the other the random number value range changes. The compiler I'm using is x-Code by the way. Here is the code:

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

int secretNumber;

int main() //integrate difficulty chooser where easy is a number b/w 1 and 10, norm 1 and 50, and hard is 1 and 100
{
    srand(static_cast<unsigned int>(time(0))); //seeds random number by time read on system

    int guess;
    int choice;

    char again = 'y';

    cout << "\tWelcome to Guess My Number\n\n";
    cout << "Please choose a difficulty:\n";
    cout << "1 - Easy\n";
    cout << "2 - Normal\n";
    cout << "3 - Hard\n";
    cin >> choice;

    while (again =='y')
    {
        int tries = 0;
        int secretNumber;
        do
        {                
            cout << "Enter a guess: ";
            cin >> guess;
            ++tries;

            switch (choice)
            {
                case 1:
                    cout << "You picked Easy.\n";
                    int secretNumber = rand() % 10 + 1;
                    break;            
                case 2:
                    cout << "You picked Normal.\n";
                    int secretNumber = rand() % 50 + 1;
                    break;
                case 3:
                    cout << "You picked Hard.\n";
                    int secretNumber = rand() % 100 + 1;
                    break;                    
                default:
                    cout << "You have made an illegal choice.\n";
            }

            if (guess > secretNumber)
            {
                cout << "\nToo high!";
            }
            else if (guess < secretNumber)
            {
                cout << "\nToo low!";
            }
            else if (guess == secretNumber && tries == 1)
            {
                cout << "\nThat's unbelievable! You guessed it in exactly 1 guess";
            }
            else
            {
                cout << "\nGreat job, you got it in just " << tries << " guesses!\n";
            }

        }
        while(guess != secretNumber);

        cout << "Do you want to play again y/n: ";
        cin >> again;
    }

    return 0;

}

The 2 errors occur in case 2 and 3 where i try to redefine the value of secretNumber.

olevegard
  • 5,294
  • 1
  • 25
  • 29
  • 2
    I'm not quite sure what the question is...? – Joachim Isaksson Aug 20 '13 at 18:48
  • Did you intend that the number change after every guess, right or wrong? Cause that's what it's doing right now. The `switch (choice)` statement should be outside the inner loop if you want one number per game, rather than per guess. – cHao Aug 20 '13 at 18:54

3 Answers3

2

The case blocks do not open different scopes, but are rather part of the same block. Your code (considering only scopes) looks somehow similar to:

int secretNumber;
{
int secretNumber = rand() %  10 + 1;
...
int secretNumber = rand() %  50 + 1;
...
int secretNumber = rand() % 100 + 1; 
}

Three different variables with the same name are being declared in the same scope, which is not allowed in the language. Note that all three declarations inside the switch would also hide the variable declared in the outer scope, which is probably not what you want anyway.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I thought you could not declare variables in a switch statement, but I have obviously not fully understood the limitations of a switch statement? Please explain. saw this one now http://stackoverflow.com/questions/1231198/declaring-variables-inside-a-switch-statement, but it was objective c. – user2672165 Aug 20 '13 at 18:54
  • @user2672165: It's complicated; you can have variables directly in a switch, because a switch is just a block with special labels and built-in `if`/`goto`. But just like with gotos, you're not allowed to jump past a variable's definition into its scope -- which effectively makes variables illegal outside of the last case. (That weirdness all but goes away when you use braces for each case that defines a variable, which creates a new scope for the variable; jumping past the case then jumps past the whole scope, so you're good.) – cHao Aug 20 '13 at 19:24
  • Yes, you can define block level variables, but in this case you do not need it. At the switch statement level, you can declare a variable only once. – Tarik Aug 20 '13 at 19:56
  • @user2672165: You can define variables, but each case is not a scope, but rather all cases are part of the same scope, and thus you cannot declare different variables with the same name. Besides my impression that you don't *want to* define 4 variables there, but just *use* the one in the outer block, you can test the creation of different variables by just renaming `secretNumber` to `secretNumber#` where `#` is the number in the `case`. That will compile without issues. – David Rodríguez - dribeas Aug 20 '13 at 20:45
  • @DavidRodríguez-dribeas: In C++? Hardly. `int main(int argc, char**) { switch (argc) { case 0:; int a = 1; break; case 1: break; }` refuses to compile in GCC or clang. "Jump to case label crosses initialization of `int a`", GCC says. – cHao Aug 20 '13 at 21:31
  • C apparently doesn't care much. But C++ absolutely does. – cHao Aug 20 '13 at 21:37
  • @cHao: You cannot jump through initialization, but you can define a variable: `switch (x) { case 1: int x; case 2: int y; default; int z; };` It is true that I should have been more precise since the code in the question performs initialization of the variables, but I still believe that this is a mistake and that he just wants to set the variable to different values in the different cases, rather than create multiple variables. – David Rodríguez - dribeas Aug 20 '13 at 21:50
  • @DavidRodríguez-dribeas: That's almost certainly the case. Common mistake, as far as i've seen. – cHao Aug 20 '13 at 21:51
0

You are getting the compile time error because you are redeclaring the same variable within the same scope (case statement block level scope). You need to delete int before secretNumber in all the case statements. Doing otherwise, the secretNumber variable declared at the while loop block level will stay undefined.

Tarik
  • 10,810
  • 2
  • 26
  • 40
0

It looks like you have some background in some other languages - perhaps a functional language and perhaps some JavaScript.

One of the key features of C++ is scoping. Variables (named value holders) have a lifetime of the scope they are within, and variables are only visible within the scope they are defined. (Not to be confused with objects, which through pointers and allocation can be teased off the stack and into heap memory, only to be lost when the variables with their address go out of scope if they are not properly deallocated).

{
    int i = 1;
}
std::cout << "i is " << i << std::endl; // compiler error, i does not exist here.
void foo() {
    int i = 1;
}
void bar() {
    foo();
    std::cout << i << std::endl;  // compiler error, i does not exist here.
}

Also, unless decorated as "const", C++ variables are mutable - they can be changed.

int i = 1;
i = 2;
std::cout << i << std::endl; // writes 2, not 1. 

So: your code is not 'redefining' secretNumber, it is shadowing the previous definition, hiding it for the duration of the current scope. Thus when you assign a value to the inner version, the "secretNumber" visible to code outside the scope is untouched.

#include <iostream>

int main()
{
    int foo = 1; // outer foo
    std::cout << "Originally, foo = " << foo << std::endl;

    {
        int foo = 2; // inner foo
        std::cout << "Inside the inner scope, foo = " << foo << std::endl;
    }

    // inner foo doesn't exist here, so it references outer foo.
    std::cout << "But the original foo still exists, " << foo << std::endl;
}

What you actually want to do is simply assign a new value to the original secretNumber variable you declared in the outer scope, since that is the only variable named "secretNumber" available to code in that scope.

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

int secretNumber;

int main() //integrate difficulty chooser where easy is a number b/w 1 and 10, norm 1 and 50, and hard is 1 and 100
{
    srand(static_cast<unsigned int>(time(0))); //seeds random number by time read on system

    int guess;
    int choice;

    char again = 'y';

    cout << "\tWelcome to Guess My Number\n\n";
    cout << "Please choose a difficulty:\n";
    cout << "1 - Easy\n";
    cout << "2 - Normal\n";
    cout << "3 - Hard\n";
    cin >> choice;

    while (again =='y')
    {
        int tries = 0;
        int secretNumber;
        do
        {                
            cout << "Enter a guess: ";
            cin >> guess;
            ++tries;

            switch (choice)
            {
                case 1:
                    cout << "You picked Easy.\n";
                    secretNumber = rand() % 10 + 1;
                    break;            
                case 2:
                    cout << "You picked Normal.\n";
                    secretNumber = rand() % 50 + 1;
                    break;
                case 3:
                    cout << "You picked Hard.\n";
                    secretNumber = rand() % 100 + 1;
                    break;                    
                default:
                    cout << "You have made an illegal choice.\n";
            }

            if (guess > secretNumber)
            {
                cout << "\nToo high!";
            }
            else if (guess < secretNumber)
            {
                cout << "\nToo low!";
            }
            else if (guess == secretNumber && tries == 1)
            {
                cout << "\nThat's unbelievable! You guessed it in exactly 1 guess";
            }
            else
            {
                cout << "\nGreat job, you got it in just " << tries << " guesses!\n";
            }

        }
        while(guess != secretNumber);

        cout << "Do you want to play again y/n: ";
        cin >> again;
    }

    return 0;

}

This is one reason why many C++ programmers choose to use prefix and suffix notations to distinguish certain types of variables:

#include <iostream>

class Foo {
public:
    int m_i;  // member variable, m_xxx
    Foo(int); // constructor taking an int.
};

static int s_i;

Foo::Foo(int i_)   // arguments use _ suffix
{
    int i = i_;    // local value of i
    i *= 3;
    m_i = i;       // we're assigning it the local value, not the argument.
}

int main()
{
    int i = 1;
    Foo foo(2);
    s_i = 3;

    std::cout << "i = "<<i<<", foo.m_i = "<<foo.m_i<<", s_i = "<<s_i<< std::endl;
}

Live Demo: http://ideone.com/dSTwPT

kfsone
  • 23,617
  • 2
  • 42
  • 74