CommonLounge Archive

Exercise: Cows and Bulls (C++)

September 11, 2018

In this exercise, we’ll implement a game in C++ known as Cows and Bulls.

If you don’t have C++ installed on your computer, you can do your work here: C++ Shell. Make sure you keep saving your work! Or, you can see C++ installation instructions here.

Overview

Here’s how the game works.

  1. The program chooses a 4-digit secret number at random, and the user is asked to guess it.
  2. User guesses the number.
  3. For each digit which is correct in position and in value, we count 1 bull. For each remaining digit which is correct in value but not position, we count 1 cow. The program outputs the number of bulls and cows, for each user guess.
  4. Examples: If secret number is 3579 and the guess is 7583, then there are 1 bulls and 2 cows. If secret number is 3556 and guess is 3575, then there are 2 bulls and 1 cows.
  5. User continues guessing till he / she guesses the correct number. Finally, the program outputs the number of turns it took the user to guess the number.

Here’s a sample interaction with the game:

Secret number chosen (between 1000 and 9999).
Start guessing! ...
444
Invalid Input!
10000
Invalid Input!
1212
Turn 1: Your guess 1212 has 1 bulls and 0 cows.
3434
Turn 2: Your guess 3434 has 2 bulls and 0 cows.
5656
Turn 3: Your guess 5656 has 0 bulls and 0 cows.
7878
Turn 4: Your guess 7878 has 0 bulls and 0 cows.
9090
Turn 5: Your guess 9090 has 1 bulls and 0 cows.
1111
Turn 6: Your guess 1111 has 0 bulls and 0 cows.
9999
Turn 7: Your guess 9999 has 1 bulls and 0 cows.
2349
Turn 8: Your guess 2349 has 0 bulls and 4 cows.
9432
Congratulations, you did it!
You took 9 to guess the number
Secret number is 9432

Step-by-step guidance

Below, we have provided step-by-step guidance for you to implement the above game.

If you want to, you can implement this exercise without any guidance (you have all the required knowledge). But please note that this is a very challenging exercise. Don’t get frustrated, divide your work into small steps, and look at what the code is doing (output variable values after every few lines and check if they are what the should be).

Step 0: Boilerplate code

Start with the boilerplate code you have been using so far in the course.

Step 1: Generating a random number

You already know how to generate a random number, as you would have done it already in previous exercise: Hotter or Colder.

For this step, write a function string generate_secret_number() which:

  1. Generates a random number between 1000 and 9999.
  2. Converts the generated number to a string and returns it.

To check that you have implemented this correctly, you can test it as follows:

int main() {
    string secret_string = generate_secret_number();
    cout << secret_string << endl;
    cout << secret_string[0] << endl;
    cout << secret_string[1] << endl;
    cout << secret_string[2] << endl;
    cout << secret_string[3] << endl;
    return 0; 
}

As you can see, converting the number to a string is making it easy for us to access the individual digits easily. We can simply do secret_string[i] to get the i-th digit (0-indexed) from the left.

You should see an output with the secret number, and then each of the 4 digits. For example, one possible output is:

3565
3
5
6
5

Run it a few times to make sure it always generates 4 digit numbers. Let the cout << secret_string << endl; line be there till you finish everything. This will help you test your work.

Step 2: Take user input and check if it is valid

For this step, write a function string get_user_input() which:

  1. Takes the user input
  2. Checks if it is valid (that is, if the inputted number lies between 1000 and 9999).
  3. If the users input is not valid, output "Invalid input!" and asks for input again.

Hint:

A while-loop would be pretty handy here.

To check that you have implemented this correctly, you can test it as follows:

int main() {
    string guess_string = get_user_input();
    cout << "Valid!" << endl; 
    return 0; 
}

To try out this code, follow this link.

Here’s the sample interaction:

444
Invalid input!
10000
Invalid input!
1212
Valid!

Aside: Why counting bulls and cows is tricky

Now, we are getting to the interesting parts - counting the number of bulls and cows.

To count the number of bulls and cows correctly, you’ll need to track which digits have been counted. Without this, it is easy very to double count (that is, count the same digit as a bull as well as a cow, or count the same digit as 2 cows, etc).

Let’s take an example. Suppose your secret number is 3556, and your input is 3575. Now you can easily calculate bulls by comparing the numbers, but for calculating cows, you will face a problem. When you compare the last element of the input i.e. 5 in this case, it is equal to the 1st index and 2nd index of the random number. But, the 1st index has already been counted as a bull. So it should not count as a cow.

Below, we guide you on how to achieve this:

Step 3: Counting bulls

For this step, write a function int count_bulls(string secret_string, string guess_string, bool secret_digit_used[], bool guess_digit_used[]) which:

  1. Counts the number of bulls (positions where both digits are equal)
  2. Updates secret_digit_used[i] and guess_digit_used[i] to true if digit i is equal (remember, arrays are passed by reference)

For example,

bool secret_digit_used[4] = {false, false, false, false};
bool guess_digit_used[4]  = {false, false, false, false};
string secret_string = "1212";
string guess_string  = "1111";
int bulls = count_bulls(secret_string, guess_string, secret_digit_used, guess_digit_used);
cout << bulls << endl; // 2 
cout << boolalpha; 
// secret_digit_used[] should be {true, false, true, false}
for (int i = 0; i < 4; i++) 
    cout << secret_digit_used[i] << ", "; 
cout << endl; 
// guess_digit_used[] should be {true, false, true, false}
for (int i = 0; i < 4; i++) 
    cout << guess_digit_used[i] << ", "; 
cout << endl; 

We are updating secret_digit_used[] and guess_digit_used[] to make sure we don’t use the thousands digit and the tens digit later when counting cows.

Make sure you output the values of the arrays and test your code thoroughly. Create multiple test cases of different types.

Step 4: Counting cows

Write a similar function as before, but this time it is called count_cows. This time, you’re counting cows. So the number of cows goes up even if the digits are not in the same position in the two numbers.

For example: If secret number is 3579 and the guess is 7583, then there are 1 bulls and 2 cows. If secret number is 3556 and guess is 3575, then there are 2 bulls and 1 cows.

Hint:

You’ll need nested for-loops.

You can check the function using the following two tests.

Test 1:

bool secret_digit_used[4] = {true, false, true, false};
bool guess_digit_used[4]  = {true, false, true, false};
string secret_string = "1211";
string guess_string  = "1113";
int cows = count_cows(secret_string, guess_string, secret_digit_used, guess_digit_used);
cout << cows << endl; // 1 
// secret_digit_used[] should be {true, false, true, true}
for (int i = 0; i < 4; i++) 
    cout << secret_digit_used[i] << ", "; 
cout << endl; 
// guess_digit_used[] should be {true, true, true, false}
for (int i = 0; i < 4; i++) 
    cout << guess_digit_used[i] << ", "; 
cout << endl; 

Test 2:

bool secret_digit_used[4] = {false, false, false, false};
bool guess_digit_used[4]  = {false, false, false, false};
string secret_string = "1212";
string guess_string  = "3131";
int cows = count_cows(secret_string, guess_string, secret_digit_used, guess_digit_used);
cout << cows << endl; // 2
// secret_digit_used[] should be {true, false, true, false}
for (int i = 0; i < 4; i++) 
    cout << secret_digit_used[i] << ", "; 
cout << endl; 
// guess_digit_used[] should be {false, true, false, true}
for (int i = 0; i < 4; i++) 
    cout << guess_digit_used[i] << ", "; 
cout << endl; 

Step 5: Counting bulls and then cows

For this step, in your main function, write a piece of code that calculates the numbers of bulls and number of cows given two numbers as strings.

For example: If secret number is 3579 and the guess is 7583, then there are 1 bulls and 2 cows. If secret number is 3556 and guess is 3575, then there are 2 bulls and 1 cows.

Step 6: Complete the overall game

Fill up the rest of the main function. Rough pseudocode:

  • Generate secret number
  • Repeat forever
  • Input guess number
  • Initialize secret_digit_used[] and guess_digit_used[]
  • Count bulls
  • Count cows
  • Check if guess is correct. If yes, break.
  • Output the information
  • Output secret number once game is over

Including again a desired sample interaction with the program:

Secret number chosen (between 1000 and 9999).
Start guessing! ...
444
Invalid input!
10000
Invalid input!
1212
Turn 1: Your guess 1212 has 1 bulls and 0 cows.
3434
Turn 2: Your guess 3434 has 2 bulls and 0 cows.
5656
Turn 3: Your guess 5656 has 0 bulls and 0 cows.
7878
Turn 4: Your guess 7878 has 0 bulls and 0 cows.
9090
Turn 5: Your guess 9090 has 1 bulls and 0 cows.
1111
Turn 6: Your guess 1111 has 0 bulls and 0 cows.
9999
Turn 7: Your guess 9999 has 1 bulls and 0 cows.
2349
Turn 8: Your guess 2349 has 0 bulls and 4 cows.
9432
Congratulations, you did it!
You took 9 to guess the number
Secret number is 9432

Congratulations

Congratulations, you made another game in C++, and a rather complicated (and fun) one at that. Keep it up!

Solution

Here’s the official solution to this exercise: C++ Solution: Cows and Bulls.


© 2016-2022. All rights reserved.