Pages

Sunday, 21 August 2016

Chapter 5 // Exercise 12, 13 - Principles & Practice Using C++

In all these exercises I am using Visual Studio Community 2015 and the header file "std_lib_facilities.h" which can be found here:


http://www.stroustrup.com/Programming/PPP2code/std_lib_facilities.h


My version is spelt differently so adjust the code accordingly if copying and pasting.


Chapter 5 // Exercise 12

Implement a little guessing game called (for some obscure reason) "Bulls and Cows". The program has a vector of four different integers in the range 0 to 9 (e.g., 1234 but not 1122) and it is the user's task to discover those numbers by repeated guesses.


Say the number to be guessed is 1234 and the user guesses 1359; the response should be "1 bull and 1 cow" because the user got one digit (1) right and in the right position (a bull) and one digit (3) right but in the wrong position (a cow). The guessing continues until the user gets four bulls, that is, has the four digits correct and in the correct order.



#include "stdafx.h"
#include "std_lib_facilities_new_version.h"
using namespace std;



int main()
try
{
vector<int> fourInts = { 1,2,3,4 };
vector<int> guess = { 0,0,0,0 };

cout << "Lets Play Bulls & Cows.\n";
cout << "Guess the 4 digit number. If a digit is right in the right position you will get a bull.\n";
cout << "If the digit is right in the wrong position you will get a cow.\n";

while (guess != fourInts)
{
for (int i = 0; i < fourInts.size(); ++i)
{
cout << "\nGuess a digit 0-9. Each number must be different: \n";
int digit;
cin >> digit;

if (digit < 0 || digit > 10)
error("You can only use 0 to 9.\n");

guess[i] = digit;
}

for (int i = 0; i < guess.size(); ++i)
{
for (int n = 0; n < guess.size(); ++n)
{
if (guess[i] == fourInts[n])
{
if (i == n)
cout << "1 Bull ";
else
cout << "1 Cow ";
}
}
}
}

cout << "\nCongratz! You did it!\n";

keep_window_open();

return 0;
}


catch (exception& e)
{
cerr << "Error: " << e.what() << '\n';
keep_window_open();
return 1;
}

I struggled on this one but it's because I'm still having a hard time wrapping my head around theoretical answers, like if n is this and i is that when n is equal to i but only if n and i are equal and at a certain time...blah blah blah. I eventually got the for loop right which has two loops:

i cycles through guess, n cycles through fourInts. So it will start with guess 1 and compare it to each number in fourInts, if it matches one at the same number(position) it'll print a bull, if it matches one but a different number, then it'll print cow. 


Chapter 5 // Exercise 13

The program is a bit tedious (no kidding) because the answer is hard-coded into the program. Make a version where the user can play repeatedly (without stopping and restarting the program) and each game has a new set of four digits. You can get four random digits by calling the random number generator randint(10) from stf_lib_facilities.h four times. You will note that if you run that program repeatedly, it will pick the same sequence of four digits each time you start the program. To avoid that, ask the user to enter a number (any number) and call seed_randint(n), also from std_lib_facilities.h, where n is the number the user entered before calling randint(10). Such an n is called a seed and different seeds give different sequences of random numbers.

#include "stdafx.h"
#include "std_lib_facilities_new_version.h"
using namespace std;

void getNum();
void stopSame();
void printRules();
void startGame();
void getDigits();
void compareDigits();
char assignLoop();
vector<int> fourInts;
vector<int> guess;

int main()
try
{
char loop = 'y';

while (loop == 'y')
{
//get number for seed  & assign numbers to fourInts
getNum();

//function which stops re-occurring numbers in fourInts
stopSame();

//print rules of game
printRules();

//begin the game and keep going till user decides to quit
startGame();

//ask if you want to play again
loop = assignLoop();
}

keep_window_open();

return 0;
}


catch (exception& e)
{
cerr << "Error: " << e.what() << '\n';
keep_window_open();
return 1;
}

void getNum()
{
cout << "Please enter a number(any number):\n";
int n;
cin >> n;

srand(n);
fourInts = { randint(9),randint(9),randint(9),randint(9) };
guess = { 0,0,0,0 };
}

void stopSame()
{
//loop which stops re-occurring numbers in fourInts
for (int i = 1; i < fourInts.size(); ++i)
{
while (fourInts[i] == fourInts[i - 1])
{
fourInts[i] = randint(9);
}
}
}

void printRules()
{
cout << "\nLets Play Bulls & Cows.\n";
cout << "Guess the 4 digit number. If a digit is right in the right position you will get a bull.\n";
cout << "If the digit is right in the wrong position you will get a cow.\n";
}

void startGame()
{
while (guess != fourInts)
{
//get user digits
getDigits();
//compare user and random numbers for bulls & cows
compareDigits();
}
}

void getDigits()
{
for (int i = 0; i < fourInts.size(); ++i)
{
cout << "\nGuess a digit 0-9. Each number must be different: \n";
int digit;
cin >> digit;

if (digit < 0 || digit > 10)
error("You can only use 0 to 9.\n");

guess[i] = digit;
}
}

void compareDigits()
{
for (int i = 0; i < guess.size(); ++i)
{
for (int n = 0; n < guess.size(); ++n)
{
if (guess[i] == fourInts[n])
{
if (i == n)
cout << "1 Bull(" << guess[i] << ")";
else
cout << "1 Cow(" << guess[i] << ")";
}
}
}
}

char assignLoop()
{
cout << "\nCongratz! You did it!\n";
cout << "Would you like to play again? y / n:\n";
char l;
cin >> l;
while (l != 'y' && l != 'n')
{
cout << "Invalid answer, try again. y / n:\n";
cin >> l;
}

return l;
}


Look at how small main is! Really proud of myself on this one and I don't care how sad that is. This was my first time using the rand function and it works!

There are a few problems with Bjarnes' instructions though:
1. seed_randint() doesn't exist in his header file, but you can use srand() from the std library.
2. By calling randint(10) it generates a number between 0 and 10. So one of your 'single' digit numbers could be 10 throwing everything off. I came up with a loop which stops that from happening and replaces any re-occurring numbers with a different random number. Call randint(9) instead.

I have a feeling though he did this on purpose since this is the chapter about error checking. I swear to god if I become a famous programmer I will hit this man with his own book when I meet him for all the rage quits he has caused me. But then I will thank him because I'm actually learning something useful.

EDIT//29.09.2016
Whilst doing exercise 8 in Chapter 6 ( a version of this that uses letters) I realised my way of stopping recurring numbers wasn't perfect. Basically it checks against the last number and changes if it's similar but what if the number generated is something like 1231? 3 and 1 are different numbers so the 1 wouldn't be replaced by a new number. The vector could be sorted first and then have any numbers replaced but it would have to keep looping until all numbers are different.

EDIT // 30/09/2019
Whilst re-doing the book I finally 'ironed' out my problem with repeating numbers by simply creating a vector containing numbers 0 - 9 with randint choosing a number to select from the vector. Once it had been added to the answers, it was then removed from the choices so it couldn't be added again.

You can check out the code at my git: https://github.com/l-paz91/principles-practice/blob/master/Chapter%205/Exercise13

11 comments:

  1. I did this by using a simple if-else formula. What do you think about it? (I know it's not completely air-tight. The reason is that it's not completely ready yet.)


    #include "../../std_lib_facilities.h"

    int main()
    try


    {
    vector secret = {3,5,6,7};
    int a, b, c, d;

    cout << "Lets play Bulls and Cows! Make a guess. ";

    while (cin >> a >> b >> c >> d)

    {

    if (secret[0] == a && secret[1] == b && secret[2] == c && secret[3] == d)

    cout << "Great guess! You win\n";

    else if (secret[0] == a && secret[1] != b && secret[2] != c && secret[3] != d)
    cout << "1 Bull \n";


    else if (secret[0] != a && secret[1] == b && secret[2] != c && secret[3] != d)

    cout << "1 Bull \n";


    else if (secret[0] != a && secret[1] != b && secret[2] == c && secret[3] != d)

    cout << "1 Bull \n";

    else if (secret[0] != a && secret[1] != b && secret[2] != c && secret[3] == d)

    cout << "1 Bull \n";


    else
    for (int i = 0; i < secret.size(); ++i)
    {
    if (secret[i] == a)
    cout << "1 Cow\n";

    if (secret[i] == b)
    cout << "1 Cow\n";

    if (secret[i] == c)
    cout << "1 Cow\n";

    if (secret[i] == d)
    cout << "1 Cow\n";
    }

    }


    keep_window_open("~");
    }

    catch (exception& e)
    {
    cerr << "error: " << e.what() << '\n';
    keep_window_open();
    return 1;
    }
    catch (...) {
    cerr << "Oops: unknown exception!\n";
    keep_window_open();
    return 2;
    }


    ReplyDelete
    Replies
    1. This is a good first attempt, however when running the programming, because you have set the "cow" portion to an else statement, it only executes if the first if statement is not true. For example, if i guessed 3 then 756, it would match to 1 bull but then skip printing the 3 cows.

      Delete
    2. Yes, you are right. I later found that this method has more problems and it became very complex when I tried to solve them. So I abandoned this method and used yours.

      Now, I am on chapter 6 now and I am not happy with it. This "Token" thing is quite difficult to grasp. I can't understand it.

      Delete
  2. Two corrections from myself and they should be like this below:

    if (digit < 0 || digit > 9)
    error("You can only use 0 to 9.\n");

    void stopSame()
    {
    //loop which stops re-occurring numbers in fourInts
    for (int i = 1; i < fourInts.size(); ++i)
    {
    while (fourInts[i] == fourInts[i - 1])
    {
    fourInts[i - 1] = randint(9);
    }
    }
    }

    ReplyDelete
    Replies
    1. No sorry, this doesn't solve the problem.

      Delete
    2. This will do:

      void stopSame() {
      for (int i = 0; i < fourInts.size(); i++) {
      for (int j = i+1; j < fourInts.size(); j++) {
      while (fourInts[i] == fourInts[j]) {
      fourInts[j] = randint(9);
      }
      }
      }
      }

      Delete
    3. This still provides the same problem, as seen below:
      http://imgur.com/cHyfwLC

      This is what happens in the loops:
      i = 0
      is 4 == 4, yes, new number 6
      is 4 == 9, no
      is 4 == 9, no

      i == 1
      is 6 == 9, no
      is 6 == 9, no

      i == 2
      is 9 == 9, yes, new number 4

      i == 3
      J is now no longer less than fourInts.size() and so the loop ends.

      I kind of addressed this error in a later post however I'm still working on a method to iron it out and it's rather annoying me. Basically, there needs to be a loop that will continue to check from the beginning to see if any numbers are the same.

      Delete
  3. Two corrections from myself and they should be like this below:

    if (digit < 0 || digit > 9)
    error("You can only use 0 to 9.\n");

    void stopSame()
    {
    //loop which stops re-occurring numbers in fourInts
    for (int i = 1; i < fourInts.size(); ++i)
    {
    while (fourInts[i] == fourInts[i - 1])
    {
    fourInts[i - 1] = randint(9); //imagine if 4 numbers are the same
    i--; //imagine if randint(9) assigns the same number again, so you need to check again, not sure if it's a good practice to just decrement the variable i, but you get the idea
    }
    }
    }

    ReplyDelete
    Replies
    1. sorry, i thought you used if-statement, ignore the i-- part.

      Delete
    2. void stopSame() {
      for (int i = 0; i < fourInts.size(); i++) {
      for (int j = i+1; j < fourInts.size(); j++) {
      while (fourInts[i] == fourInts[j]) {
      fourInts[j] = randint(9);
      }
      }
      }
      }

      Delete
  4. This is for question 12. Works pretty well. Need to see if I can find a way to allow duplicates but that'll probably be a later issue:

    int main(){
    vector< int > secret = {2, 3, 8, 0};
    vector< int > guesses;
    int guess, bull = 0, cow = 0, counter = 0;
    cout << "Please input 4 integer guesses, no duplicates.\n";
    while(secret != guesses){
    ++counter;
    for(int n = 0; n < secret.size(); ++n){
    cin >> guess;
    if(guess < 0 || guess > 10)
    error("Invalid integer");
    guesses.push_back(guess);
    }
    for(int i = 0; i < secret.size(); ++i){
    if(guesses[i] == secret[i]){
    ++bull;
    }
    else{
    for(int j = 0; j < guesses.size(); ++j){
    if(secret[i] == guesses[j]){
    ++cow;
    }
    }
    }
    }
    if(bull > 1 && cow > 1)
    cout << "\n" << bull << " bulls and " << cow << " cows\n";
    else if(bull > 1 && cow < 1)
    cout << "\n" << bull << " bulls\n";
    else if(bull > 1 && cow == 1)
    cout << "\n" << bull << " bulls and " << cow << " cow\n";
    else if(bull == 1 && cow < 1)
    cout << "\n" << bull << " bull\n";
    else if(cow > 1 && bull < 1)
    cout << "\n" << cow << " cows\n";
    else if(cow > 1 && bull == 1)
    cout << "\n" << bull << " bull and " << cow << " cows\n";
    else if(cow == 1 && bull < 1)
    cout << "\n" << cow << " cow\n";
    else if(bull == 1 && cow == 1)
    cout << "\n" << bull << " bull and " << cow << " cow\n";
    else
    cout << "\nYou are retarded\n";
    cow = 0;
    bull = 0;
    if(secret != guesses)
    guesses.clear();
    }
    cout << "\nCongrats you won.\n";
    cout << "It took you " << counter << " tries";
    }

    ReplyDelete