1. #1
    Legendary! MonsieuRoberts's Avatar
    10+ Year Old Account
    Join Date
    May 2010
    Location
    Weeping Squares, Vilendra, Solus
    Posts
    6,621

    A C++ question regarding voids, loops and cin

    Hey guys,

    So I'm trying to use some voids and loops (no arrays yet) to modify a simple memory game I made for a class last week. The game has 4 cards, there's 2 sets of symbols underneath. User picks 4 cards total, I tell them how many pairs they got after 4 turns. We didn't know about loops yet so I hard-coded every single option last time with a bunch of if/else statements and a switch, and while it worked, it was obviously a TON of work. Almost 1000 lines for a simple pick two matching symbols game.

    This time I'm allowed to use loops, and there's a few new conditions to meet; Make the user keep guessing until they guess everything, and don't hard code the results of the user's choices. Use loops instead.

    While I'm familiar with loops because of some other assignments and prep work I've done, and can comfortably use them, I'm having trouble with, well, not hard coding the results. I'm not quite sure how to take the user's guesses and reflect them in a void call. At least, I don't think I know how.

    If I have a void statement such as this, how exactly could I go about assigning the user's guess to the corresponding card?

    Code:
    void printCards(char card1, char card2, char card3, char card4)
    {
    	cout << "Your cards: " << endl;
    	cout << "[" << card1 << "]" << "  " << "[" << card2 << "]" << endl;
    	cout << "[" << card3 << "]" << "  " << "[" << card4 << "]" << endl;
    }
    My mind immediately wants to just do some if/else statements. "If cin == card1, flip card 1, show symbol. Else if cin == card2, flip card 2, show symbol." etc. I really feel like this is the wrong choice however, and I feel that way because my prof briefly showed us her code for doing this in a much more elegant way, automagically assigning the user's first guess to one of the constraints in the void function. I just don't know how she did it. She couldn't have done cin >> guess1; guess1 = card 1,2,3 or 4; because I can't predictably assign the user's guess to one of the constraints. I dunno what the user's going to do, which card they're going to guess.

    I need some guidance here, as I feel like I haven't learned something or am forgetting something that's integral to doing this the quick and simple way, the programmer's way. I've re-read the last few chapters of my textbook hoping the answer would reveal itself to me but it hasn't. What is it that I'm missing? How exactly can I assign a user's cin to one of several variables in a void call, reflecting the user's choice of card? Do I have to use some if/else here?
    Last edited by MonsieuRoberts; 2017-10-19 at 05:21 AM.
    ⛥⛥⛥⛥⛥ "In short, people are idiots who don't really understand anything." ⛥⛥⛥⛥⛥
    [/url]
    ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥ ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥

  2. #2
    Deleted
    Well it seems you're kind of missing one point of using functions, i.e. that you can call them repeatedly with different parameters.

    So you would create a function that takes the 4 cards the user picked as parameters and generates the appropriate response - this usually requires if/switch statements.
    Then you can call that function repeatedly (e.g. from a loop) and use the input the user provided as parameters.

    A (contrived) simple example:

    Code:
    // this is a function that takes two int parameters multiplying and dividing them (if possible) and displays the result 
    void MulDivNumbers(int a, int b)
    {
    	cout << a << " times " << b << " equals " << a*b << endl;
    
    	if (b == 0)
    		cout << "Cannot divide by zero" << endl;
    	else
    		cout << a << " divided by " << b << " yields " << a/b << " with " << a%b << " remaining" << endl;
    
    	cout << endl;
    }
    
    int main()
    {
    	MulDivNumbers(14, 4); // call the function
    	MulDivNumbers(12, 0); // call it again
    
    // and now in a loop
    	for (int i = 0; i < 5; i++)
    		MulDivNumbers(30, i);
    
    // with user input
           int input;
           cout << "Enter a number :";
           cin >> input;
           MulDivNumbers(30, input);
    
        return 0;
    }

  3. #3
    Epic! Pejo's Avatar
    10+ Year Old Account
    Join Date
    Mar 2011
    Location
    C eh N eh D eh
    Posts
    1,555
    That's a good reply above. I don't fully understand your constraints, so I ask forgiveness. When you say loops, are you restricted to just For loops or are you able to do While loops as well? When you get these problems, break them up to make it easier to manage.

    Memory gäme with 4 cards, made up of two symbols. User guesses two cards, and you see if they match. Keep going until game is done. Flow diagram looks like this:
    New Game - Generate new set of cards. I.e. assign values for card 1 - 4 with values.
    Ask user for two guesses. Store in guess 1 and 2
    Check if match. Use above code as a hint for this.
    If match, mark cards as matched and check if game is over
    Loop back until ask for guesses.

    For me, if you can use While, set a boolean to gameFinished = false and do while gameFinished is false.

  4. #4
    Legendary! MonsieuRoberts's Avatar
    10+ Year Old Account
    Join Date
    May 2010
    Location
    Weeping Squares, Vilendra, Solus
    Posts
    6,621
    Quote Originally Posted by lloewe View Post
    Well it seems you're kind of missing one point of using functions, i.e. that you can call them repeatedly with different parameters.

    So you would create a function that takes the 4 cards the user picked as parameters and generates the appropriate response - this usually requires if/switch statements.
    Then you can call that function repeatedly (e.g. from a loop) and use the input the user provided as parameters.

    A (contrived) simple example:

    Code:
    // this is a function that takes two int parameters multiplying and dividing them (if possible) and displays the result 
    void MulDivNumbers(int a, int b)
    {
    	cout << a << " times " << b << " equals " << a*b << endl;
    
    	if (b == 0)
    		cout << "Cannot divide by zero" << endl;
    	else
    		cout << a << " divided by " << b << " yields " << a/b << " with " << a%b << " remaining" << endl;
    
    	cout << endl;
    }
    
    int main()
    {
    	MulDivNumbers(14, 4); // call the function
    	MulDivNumbers(12, 0); // call it again
    
    // and now in a loop
    	for (int i = 0; i < 5; i++)
    		MulDivNumbers(30, i);
    
    // with user input
           int input;
           cout << "Enter a number :";
           cin >> input;
           MulDivNumbers(30, input);
    
        return 0;
    }
    Quote Originally Posted by Pejo View Post
    That's a good reply above. I don't fully understand your constraints, so I ask forgiveness. When you say loops, are you restricted to just For loops or are you able to do While loops as well? When you get these problems, break them up to make it easier to manage.

    Memory gäme with 4 cards, made up of two symbols. User guesses two cards, and you see if they match. Keep going until game is done. Flow diagram looks like this:
    New Game - Generate new set of cards. I.e. assign values for card 1 - 4 with values.
    Ask user for two guesses. Store in guess 1 and 2
    Check if match. Use above code as a hint for this.
    If match, mark cards as matched and check if game is over
    Loop back until ask for guesses.

    For me, if you can use While, set a boolean to gameFinished = false and do while gameFinished is false.
    I'm sorry, I wasn't very clear with my description of the goal of the program there. Lemme try again:

    -Program starts and prints out a welcome, rules of the game, and instructions
    -User has 4 cards to choose from. There are 2 pairs of $ and 2 pairs of @. User must guess the pairs one at a time and keep guessing until they have all pairs.
    -I cin the user's first guess and reflect that change by flipping the card they chose.
    -I cin the user's second guess and check of it's a match:
    -If it is, I let them keep guessing and keep their guessed cards flipped over.
    -If it isn't, all cards are flipped to default positions and they begin guessing again.
    -The game ends once all cards are guessed.

    I've learned about if/else and switch statements, and have learned about While, Counter While, Flag While, Sentinel, Do/While and For loops. I don't think my understanding of loops is the issue, I feel pretty comfortable using them or knowing what to use them for.

    What I'm having a tough time with is reflecting the user's choices. Right now I'm doing something like this:


    Code:
    //I have a terrible looking but functional void here to print out a nice looking group of cards. 2x2 grid.
    void printCards (char card1, char card2, char card3, char card4)
    
    #include <iostream>
    #include <iomanip>
    
    using namespace std;
    
    //Just declaring these symbols as constants to expose myself to more constant usage.
    const char front = '#';
    const char symbol1 = '$';
    const char symbol2 = '@';
    const int oneMatch = 1;
    const int twoMatch = 2;
    
    int main()
    {
    	//My Declarations
    	int guess1 = 0;
    	int guess2 = 0;
    	int matches = 0;
    	int guessCount = 0;
    
    printCards(front, front, front, front); 
    	cout << endl;
    	cout << "Total guesses: " << guessCount << "." << endl;
    
    	cin >> guess1;
    	system("CLS");
    
    	while (oneMatch > matches);
    	{
    		if (guess1 = 1) //If the user guesses card 1, flip the first card, increment our guess counter (just for a grand total at the end) and cin their second guess
    		{
    			printCards(symbol1, front, front, front); //I don't know why this is left-aligned in the code here but it's not in VS.
    			cout << endl;
    			guessCount++;
    			cout << "Total guesses: " << guessCount << "." << endl;
    			cout << "Let's see if you can get a match, pick another card." << endl << endl;
    
    			cin >> guess2;
    			system("CLS");
    
    			if (guess2 = 4) //If their second guess would be a pair based on the first one, increment our guess counter and increment our match counter, which would end the While loop.
    			{
    				printCards(symbol1, front, front, symbol1);
    				cout << endl;
    				cout << "Yooo you matched two $ cards. Noice. Let's see if you can match the other two cards." << endl;
    				guessCount++;
    				matches++;
    			}
    			else
    			{
    				printCards(front, front, front, front); //Otherwise I tell 'em they didn't get a match, matches isn't incremented, and the while loop repeats.
    				cout << endl;
    				cout << "Hoooo sorry, no match for you. Try again though." << endl;
    				guessCount++;
    			}
                     }
                  else if (guess1 = 2) //These would basically be the exact same as the code above. Just condensed them for this thread.
                  else if (guess1 = 3)
                  else if (guess1 = 4)
                  else //This is just a "Invalid entry, try again" thing.
    	}
    	system("pause");
    	return 0;
    }
    The rest of my code so far is "well what if guess 1 = 2? what is guess 1 = 3? = 4?" and continuing the thought process above to satisfy all answers.

    I can't help but feel that this is the wrong way of doing this. My profs are unreachable atm and I can't really do much else except try more code or ask people. I can get this done this way, I did the entire thing without any loops to begin with, I'm jut looking for the best way.
    Last edited by MonsieuRoberts; 2017-10-19 at 07:23 PM.
    ⛥⛥⛥⛥⛥ "In short, people are idiots who don't really understand anything." ⛥⛥⛥⛥⛥
    [/url]
    ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥ ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥

  5. #5
    You are misusing the term "void". Void is a type in C++, it's a "no type" type. You have a function that returns no value, meaning it returns void. You need several things:

    A condition to loop. You've stated it yourself "The game ends once all cards are guessed.". Something along the lines of

    while(total_guess < 2 /* number of pair guesses */) {...}
    You also need things to do at each iteration, namely:

    - get input
    - check if it's a match
    - if it is a match, increase the total_guess counter
    - if not, keep taking input

    For a generalized solution, you're gonna need a way to remember which cards you've flipped so that in your loop, you pass that information to your print function. The easiest solution is storing a boolean for each card, whenever a card is chosen, set the boolean. So in your loop,

    - ask for input
    - identify which card is flipped, and set it
    - print cards
    - apply your winning condition logic

    Do not forget to unset cards you've flipped when player fails to guess a pair. Finally, since all you need is the current and previous guesses, you can compare them to see if its a pair match or not.

    - - - Updated - - -

    You are in the right way to be a good programmer. Always be critical of your code and when "it doesn't feel right", it probably is not a good solution.
    Last edited by Kuntantee; 2017-10-19 at 09:26 PM.

  6. #6
    Epic! Pejo's Avatar
    10+ Year Old Account
    Join Date
    Mar 2011
    Location
    C eh N eh D eh
    Posts
    1,555
    Kuntantee has some good advice for you. I will recommend one change off the bat that you will hopefully help make this easier: make a method for each thing you're trying to do. Have a method that shows current setup of cards, and have one that will flip a card. Some pseudocode for my recommendation.

    While (gameNotFinished){
    Get Guess1
    FlipCard(Guess1)
    ShowCurrentCards
    Get guess2 - ensure guess2 is not guess1
    FlipCard(guess2)
    If guess1 symbol = guess2 symbol, increment matchCount
    Increment GuessCount
    If matchCount = maxMatchCount (2) gameFinished = true
    }

  7. #7
    Deleted
    I don't know if you have talked about state machines in your lectures but basically the problem is represented by a finite number of states a system can be in and the available transitions between those states.
    In your nested if statement solution the state is defined by the location of the code that is currently executing and as you've experienced with lots of states and transitions the code base explodes. This is why you normally store the state as data (variables) and the code "only" manages the transitions. The advantage here is that you don't care how you got to the machine's state.

    Now I'm a bit surprised you're not allowed to use arrays because this problem would really benefit from one, however it can be solved even tough it's a bit awkward. Technically you could also use the bits to store which cards are turned but that's kind of like an array so better not use it.


    Anyway here's my quick stab at the game (or how I understood it).
    It's certainly not elegant nor flexible with all the hard coding, but I think there's really no point of going out of my way to do that given the restraints.

    Code:
    
    
    const char symbol1 = '@';
    const char symbol2 = '$';
    const char backside = '#';
    int matches = 0;
    
    bool card1turned = false;
    bool card2turned = false;
    bool card3turned = false;
    bool card4turned = false;
    int previousPick = 0;
    
    void printCards()
    {
    
    	if (card1turned) cout << symbol1; else cout << backside;
    	if (card2turned) cout << symbol2; else cout << backside;
    	if (card3turned) cout << symbol2; else cout << backside;
    	if (card4turned) cout << symbol1; else cout << backside;
    	cout << endl;
    }
    
    // if you haven't talked about functions returning values you can 
    // easily inline this code into pickCard since it is only called once
    bool isTurned(int i)
    {
    	switch (i)
    	{
    	case 1:
    		return card1turned;
    	case 2:
    		return card2turned;
    	case 3:
    		return card3turned;
    	case 4:
    		return card4turned;
    	default:
    		return false;
    	}
    }
    
    // this and the previous function would become simple statements if you were to use an array
    // void turnCard(int i) { cards[i]=!cards[i];}
    void turnCard(int i)
    {
    	switch (i)
    	{
    	case 1:
    		card1turned = !card1turned;
    		break;
    	case 2:
    		card2turned = !card2turned;
    		break;
    	case 3:
    		card3turned = !card3turned;
    		break;
    	case 4:
    		card4turned = !card4turned;
    		break;
    	}
    }
    
    void pickCard(int card)
    {
    	if (isTurned(card))
    	{
    		cout << "you've already turned that card";
    		return;
    	}
    	turnCard(card);
    	printCards();
    
    	if (previousPick == 0)
    	{
    		cout << "Now pick another card" << endl;
    		previousPick = card;
    		return;
    	}
    
    	//this would get really complex with more cards and without arrays
    	if ((card == 1 && previousPick == 4) || (card == 4 && previousPick == 1) ||
    		(card == 2 && previousPick == 3) || (card == 3 && previousPick == 2))
    	{
    		cout << "matched!" << endl;
    		matches++;
    	}
    	else
    	{
    		cout << "Sorry, no match. Try again" << endl;
    		turnCard(card);
    		turnCard(previousPick);
    	}
    	previousPick = 0;
    }
    
    
    
    int main()
    {
    	int pick;
    	while (matches < 2)
    	{
    		cout << endl;
    		printCards();
    		cout << "Pick a card (1-4):";
    		cin >> pick;
    		if (pick > 0 && pick < 5)
    			pickCard(pick);
    	}
    	cout << "Congratulations" << endl;
    
    	return 0;
    }
    
    

  8. #8
    Blademaster Cyrako's Avatar
    7+ Year Old Account
    Join Date
    Nov 2014
    Location
    New Zealand
    Posts
    48
    Not being able to use an array is well difficult, good luck with this why so many restrictions ?

  9. #9
    Quote Originally Posted by Cyrako View Post
    [...] why so many restrictions ?
    This is what I thought as well. Probably his/her professor did not cover arrays yet. I wouldn't personally count that as an impediment to students using advanced tools in their assignments. Alternatively, the professor is forcing students to do mental gymnastic, which is a good thing for students taking programming courses. This problem is harder to solve when you are not allowed to use arrays.

  10. #10
    Legendary! MonsieuRoberts's Avatar
    10+ Year Old Account
    Join Date
    May 2010
    Location
    Weeping Squares, Vilendra, Solus
    Posts
    6,621
    Quote Originally Posted by Kuntantee View Post
    This is what I thought as well. Probably his/her professor did not cover arrays yet. I wouldn't personally count that as an impediment to students using advanced tools in their assignments. Alternatively, the professor is forcing students to do mental gymnastic, which is a good thing for students taking programming courses. This problem is harder to solve when you are not allowed to use arrays.
    Based on my past few assignments, it seems like my prof really wants us to have a rock solid fundamental understanding of how these functions work, then introduces ways for us to simplify our thinking. When someone expresses a sense that "this just doesn't seem like a good way of doing this" there's always a quicker, faster, simpler, more logical way revealed next class, or next chapter.

    I like it, because it forces me to really think about all the ways I can tackle the problem at hand given limited resources. I knew I could use voids to reproduce something I'm going to be printing over and over, it was just some smaller details that I was missing to put all the pieces together.

    The only reason I know about Arrays was because I was fed up with that missing nugget of knowledge and decided to look elsewhere to see how others could approach a solution, and it looks like arrays will dramatically simplify my code. Which is good!

    The code and suggestions provided have been really helpful. You didn't do my project for me or even directly write out what I'm supposed to do, but you cleared up a gap in my understanding of voids, and helped streamline how I think about preparing pseudocode. Thanks for your responses and opinions!
    ⛥⛥⛥⛥⛥ "In short, people are idiots who don't really understand anything." ⛥⛥⛥⛥⛥
    [/url]
    ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥ ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥

  11. #11
    You are welcome. Feel free to ask questions via PM.

  12. #12
    Legendary! MonsieuRoberts's Avatar
    10+ Year Old Account
    Join Date
    May 2010
    Location
    Weeping Squares, Vilendra, Solus
    Posts
    6,621
    Quote Originally Posted by Kuntantee View Post
    You are welcome. Feel free to ask questions via PM.
    I'll keep that in mind and promise not to abuse the offer.
    ⛥⛥⛥⛥⛥ "In short, people are idiots who don't really understand anything." ⛥⛥⛥⛥⛥
    [/url]
    ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥ ⛥⛥⛥⛥⛥⛥⛥⛥⛥⛥

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •