Tuesday 23 August 2016

Chapter 5 // Exercise 14 - 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 14

Read (day-of-the-week,value) pairs from the standard input. For example:

Tuesday 23 Friday 56 Tuesday -3 Thursday 99

Collect all the values for each day of the week in a vector<int>.Write out the values of the seven day-of-the-week vectors. Print out the sum of the values in each vector. Ignore illegal days of the week such as Funday, but accept common synonyms such as Mon and monday. Write out the number of rejected values.

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

void getData();
int checkIllegalDotw();
void printVectors();
void printSum();
vector<int> values;
vector<string> dotw;
vector<string> acceptedDays = { 
"Monday", "monday", "mon", "Mon",
"Tuesday", "tuesday", "tue", "Tue",
"Wednesday", "wednesday", "wed", "Wed",
"Thursday", "thursday", "thurs", "Thurs",
"Friday", "friday", "fri", "Fri",
"Saturday", "saturday", "sat", "Sat",
"Sunday", "sunday", "sun", "Sun" };
int g_rejectedValues = 0;
string g_inputDotw;
int g_inputValues;
int g_n = 0;

int main()
try
{
cout << "Please enter Day of The Week followed by value. Enter from Monday to Sunday: \n";

//get days of the week & values
getData();

//print out both vectors
printVectors();

//print sum & rejected values
printSum();

keep_window_open();

return 0;
}


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

//this gets input & checks if g_inputDotw is == to anything within acceptedDays
void getData()
{
while (dotw.size() < 7)
{
cout << "Day of The Week: ";
cin >> g_inputDotw;

g_n = checkIllegalDotw();

while (g_n == 0)
{
cout << "Sorry, that is not an accepted Day of The Week. Try again: \n" << endl;
++g_rejectedValues;
cin >> g_inputDotw;
g_n = checkIllegalDotw();
}

cout << "Value: ";
cin >> g_inputValues;

dotw.push_back(g_inputDotw);
values.push_back(g_inputValues);
}

cout << '\n';
}

//if n is equal to 0 then inputDotw made no matches to anything in acceptedDays
int checkIllegalDotw()
{
for (int i = 0; i < acceptedDays.size(); ++i)
{

if (g_inputDotw == acceptedDays[i])
{
++g_n;
}
}

return g_n;
}

void printVectors()
{
for (int x = 0; x < dotw.size(); ++x)
{
cout << dotw[x] << '\t' << values[x] << '\n';
}
}

void printSum()
{
int sum = 0;

for (int i = 0; i < values.size(); ++i)
{
 
sum += values[i];
}

cout << "The sum of the values is: " << sum << '\n';
cout << "Amount of Week days rejected: " << g_rejectedValues << '\n';
}

It was the omitting illegal days that got me stuck on this one however eventually I created a function which compares the input to each member of the vector that contains allowed days of the week. Basically if inputDotw makes a match to any of those numbers g_n increases making it no longer equal to 0. If it is equal to 0 you know that the input was wrong. I suppose I could've used a bool function for this instead but I still don't feel 100% confident using them yet.

Also, for anyone wondering about the g_ prefix on some variables, it's just to denote that it is a global variable and can therefore be accessed in any part of the code.

EDIT 18/03/2018
As part of exercise 11 in Chapter 7, you are told to revisit 2 programs from chapters 4 or 5. I chose this one as I didn't realise just how much not using bools was holding me back. Here is the new code for this exercise:

#include "stdafx.h"
#include "std_lib_facilities.h"
#include <windows.h>

class ValuePair
{
private:
 //create a name value pair variable
 struct DayValue
 {
  int value;
  string day;
 };

public:
 //functions
 void getValues();
 void printVectors();

 //variables
 DayValue dayvalue;                 //holds int and string pair
 vector<DayValue> dayValueVector;   //vector to store days and values created from struct
 vector<string> acceptedDays = {     //list of days allowed
  "Monday", "monday", "mon", "Mon",
  "Tuesday", "tuesday", "tue", "Tue",
  "Wednesday", "wednesday", "wed", "Wed",
  "Thursday", "thursday", "thurs", "Thurs",
  "Friday", "friday", "fri", "Fri",
  "Saturday", "saturday", "sat", "Sat",
  "Sunday", "sunday", "sun", "Sun" };
 
 int rejectedValues;
 string input;
 int values;
};

//get values, checks for illegal input
void ValuePair::getValues()
{
 bool stop = false;      //to stop loop
 bool allowedDay = false;
 cout << "Please enter a Day of the Week followed by a value. Press 'q' in day to stop.\n";

 //while not 'q' keep getting entries
 while (!stop)
 {
  allowedDay = false;   //reset bool for loop
  cout << ">Day: ";
  cin >> input;

  //if input is q, exit loop to main
  if (input == "q")
  {
   stop = true;
   return;
  }
  else
  {
   //check to see if day is allowed, if allowed set to true, if not ++ rejected value
   for (int i = 0; i < acceptedDays.size(); ++i)
   {
    if (input == acceptedDays[i])
    {
     allowedDay = true;
     dayvalue.day = input;
    }
   }

   //get a value
   cout << ">Value: ";
   cin >> values;
   //if not a number, keep looping til number entered
   while (!cin)
   {
    ++rejectedValues;
    cin.clear();    //reset stream
    cin.ignore(10000, '\n'); //ignore everything entered till newline
    cout << "Not a number, try again > ";
    cin >> values;
   }

   //if day was valid pushback both values, if not ++rejected values and don't pushback
   if (allowedDay)
   { 
    dayvalue.value = values;
    dayValueVector.push_back(dayvalue);  //push values back into vector
   }
   else
    ++rejectedValues;
  }
 }
}

//print the valid values in vector as well as sum and rejected values
void ValuePair::printVectors()
{
 int value = 0;

 for (int i = 0; i < dayValueVector.size(); ++i)
 {
  cout << "Day: " << dayValueVector[i].day << "   Value: " << dayValueVector[i].value << endl;
  value += dayValueVector[i].value;
 }

 cout << "\nSum of all Values: " << value << endl;
 cout << "Number of Rejected Values: " << rejectedValues << endl;
}

ValuePair dayAndValue;

//start main program
int main()
{
 //get values and check them
 dayAndValue.getValues();

 //print the vector and the sum of all values
 dayAndValue.printVectors();

 //keep the the window open
 keep_window_open();

 return 0;
}


This now handles all errors. Also instead of the very confusing way I was checking for illegal input before, now I check the input against the allowed values and if it finds one, it sets a bool to true. Only when this bool is true will it pushback both values into the vector, if not it will still ask for a value but ignore them as per what is asked for.

EDIT 30/09/2019

What a mess these previous answers were. Here is the new code: https://github.com/l-paz91/principles-practice/blob/master/Chapter%205/Exercise14

It doesn't have fancy checking, i.e it expects numbers for number input (or it will throw a runtime error) but I have no idea what was going through my mind 3 years ago.

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

Friday 19 August 2016

Chapter 5 // Exercise 11 - 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 11

Write a program that writes out the first so many values of the Fibonacci series, that is, the series that starts with 1 1 2 3 5 8 13 21 34. The next number of the series is the sum of the previous ones. Find the largest Fibonacci number that fits in an Int.

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



int main()
try
{
vector<int> fibonacci = { 1,1 };

for (int i = 1; i < 100; ++i)
{
int sum = fibonacci[i] + fibonacci[i - 1];
fibonacci.push_back(sum);
}

for (int i = 0; i < fibonacci.size(); ++i)
{
cout << fibonacci[i] << '\n';
}

keep_window_open();

return 0;
}


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

}

This was pretty easy however I don't know if it's cheating by including the first 2 numbers.  i starts at 1 on the first for loop because if it didn't we'd be going out of range on the vector because i[1] is equal to the 2nd value in Fibonacci.

The largest number (on my compiler) that would fit into an int was 1,863,311,903.

Wednesday 17 August 2016

Chapter 5 // Exercise 8, 9, 10 - 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 8

Write a program that reads and stores a series of integers and then computes the sum of the first N integers. First ask for N, then read the values into a vector, then calculate the sum of the first N values. For example:

"Please enter the number of values you want to um:"
3
"Please enter some integers (press | to stop):"
12 23 13 24 15 |
"The sum of the first 3 numbers (12 23 13 ) is 48."

Handle all inputs. For example, make sure to give an error message if the user asks fors the sum of more numbers than there are in the vector.

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

double getSum();
double g_noValues;
vector<double> g_values;

int main()
try
{
cout << "Please enter the number of values you want to sum: \n";
cin >> g_noValues;

cout << "Please enter some integers (press enter then ctrl+z and enter again to stop): \n";
double n;
while (cin >> n)
{
g_values.push_back(n);
}

double sumN = getSum();

cout << "The sum of the first " << g_noValues << " numbers ( ";
for (int i = 0; i < g_noValues; ++i)
{
cout << g_values[i] << " ";
}
cout << " ) is " << sumN << '\n';

keep_window_open();

return 0;
}


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


double getSum()
{
double n = 0;
if (g_noValues > g_values.size())
{
error("You haven't entered enough numbers, sorry.\n");
}

for (int i = 0; i < g_noValues; ++i)
{
n += g_values[i];
}
return n;
}

Now, I know he says to use '|' to stop input but on my machine this just made it skip all the code and close the window immediately without showing the results, so I reverted to ctrl+z instead.

Chapter 5 // Exercise 9

Modify the program from exercise 8 to write out an error if the result cannot be represented in an int.

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

int getSum();
double g_noValues;
vector<double> g_values;

int main()
try
{
cout << "Please enter the number of values you want to sum: \n";
cin >> g_noValues;

cout << "Please enter some integers (press enter then ctrl+z and enter again to stop): \n";
double n;
while (cin >> n)
{
g_values.push_back(n);
}

int sumN = getSum();

cout << "The sum of the first " << g_noValues << " numbers ( ";
for (int i = 0; i < g_noValues; ++i) //print out first n values
{
cout << g_values[i] << " ";
}
cout << " ) is " << sumN << '\n';

keep_window_open();

return 0;
}


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


int getSum()
{
int nTest = 0;
double n = 0;
if (g_noValues > g_values.size())
{
error("You haven't entered enough numbers, sorry.\n");
}

for (int i = 0; i < g_noValues; ++i)
{
n += g_values[i];
nTest += g_values[i]; //for comparison to see if it can fit
}
if (nTest != n)
{
error("Sorry, this number cannot be represented in an int.\n");
}
return nTest;
}

This one was quite simple. In order to find out if the sum couldn't fit into an int you just create an int and a double containing the same number and then compare them to see if they match. If they don't obviously the int has been truncated.


Chapter 5 // Exercise 10

Modify the program from exercise 8 to use double instead of int. Also, make a vector of doubles containing the N-1 differences between adjacent values and write out that vector of differences.

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

void getSum();
void getDiffs();
double g_noValues;
vector<double> g_values;
vector<double> g_valDif;

int main()
try
{
cout << "Please enter the number of values you want to sum: \n";
cin >> g_noValues;

cout << "Please enter some integers (press enter then ctrl+z and enter again to stop): \n";
double n;
while (cin >> n)
{
g_values.push_back(n);
}

getSum();
getDiffs();

keep_window_open();

return 0;
}


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

//this returns the sum of the first n values
void getSum()
{
double n = 0;
if (g_noValues > g_values.size())
{
error("You haven't entered enough numbers, sorry.\n");
}

for (int i = 0; i < g_noValues; ++i)
{
n += g_values[i];
}

cout << "The sum of the first " << g_noValues << " numbers ( ";

for (int i = 0; i < g_noValues; ++i) //print out first n values
{
cout << g_values[i] << " ";
}
cout << " ) is " << n << '\n';
}

//this gets and prints the differences. Also it always prints positive numbers
void getDiffs()
{
double diff = 0;

for (int i = 1; i < g_values.size(); ++i)
{
if (g_values[i] < g_values[i - 1])
{
diff = g_values[i - 1] - g_values[i];
g_valDif.push_back(diff);
}
else
{
diff = g_values[i] - g_values[i - 1];
g_valDif.push_back(diff);
}
}

cout << "\nThe differences between each numbers are: \n";

for (int i = 0; i < g_valDif.size(); ++i)
{
cout << g_valDif[i] << " ";
}

cout << '\n';
}

I changed things up a little bit in this one and made getting the sum into a function as well. Getting the differences is straightforward enough, I just stuck an if statement in there so the number is always a positive.