How should I write an eloquent continue? - c++

So its often come up in my classes that i need to be able to have a program where the user can do things multiple times.
so I write something like
boolean continue=true;
while (continue) {
//do code
boolean isAnswerGood=false;
while (!isAnswerGood) {
cout << "Do you wish to continue" << endl;
string answer;
getline(cin, answer);
if (answer=="y") //or something else
isAnswerGood=true;
else if (answer=="n") {
isAnswerGood=true;
continue=false;
} else
cout << "That wasnt "y" or "n" please type something again" << endl;
}
}
This seems bloated and a lot of code for something so simple. Im willing to think outside of the box with this one, so if anyone can give me a clue as to a better solution to this, id appreciate it.

Break it into separate functions. (Almost always the answer to "how do I write an eloquent...?")
For example:
do {
// something
} while (UserWantsMore());
/////////
bool UserWantsMore() {
std::string answer = GetAnswer("Continue?");
while (!GoodAnswer(answer))
answer = GetAnswer("Please answer 'yes' or 'no'!");
return IsAnswerYes(answer);
}
bool GoodAnswer(const std::string& answer) {
return !answer.empty() && (answer[0] == 'y' || answer[0] == 'n');
}
bool IsAnswerYes(const std::string& answer) {
return !answer.empty() && answer[0] == 'y';
}
std::string GetAnswer(const char* prompt) {
std::cout << prompt << std::cend;
std::string answer;
std::getline(std::cin, answer);
return answer;
}

Sorry, but that is about the right amount of code. C and C++ are relatively verbose compared to some other languages.
However, if this is a common problem, you can abstract it out and make a function out of it. The function would be very similar to what you already have, but then you would call it something like this:
boolean again = true;
while (again) {
// do code
again = ask_user("Do you wish to continue?", "y", "n");
}
The arguments to ask_user() should be the question to ask, the answer to accept that means "yes", and the answer to accept that means "no". The error message (about an unexpected input) can be made using the second and third arguments, so we don't really need to pass it in.
Of course, the problem could be even more complicated than this... what if your code will be used by non-English speakers? If you needed to handle localization, you could make a basic function that has all strings passed in including the error message, and then make a wrapper that the user calls with a language specification. Here's an example, this time using the "infinite loop" in C++:
for (;;) {
// do code
if (!ask_user_yesno("en_us")) // locale: English, USA
break;
}

If you are just wanting to make what you already have do the same thing with a little less code, this will work.
string answer="y";
while (answer=="y") {
//do code
for(;;) {
cout << "Do you wish to continue" << endl;
getline(cin, answer);
if ((answer=="y")||(answer=="n")) break;
cout << "That wasnt \"y\" or \"n\" please type something again" << endl;
}
}
Slightly less code, but slightly more obfuscated:
string answer="y";
while (answer!="n") {
if (answer=="y") {
//do code
} else {
cout << "That wasnt \"y\" or \"n\" please type something again" << endl;
}
cout << "Do you wish to continue" << endl;
getline(cin, answer);
}
Here's a version that uses <termios.h> to get the answer. It uses more code, but behaves more "eloquently".
int getch (void)
{
int c;
struct termios oldt;
struct termios newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
return c;
}
bool again (std::string prompt, std::string yes, std::string no)
{
bool doyes = false;
bool dono = false;
for (;;) {
std::cout << prompt;
int c = getch();
std::cout << std::endl;
doyes = (yes.find(c) != yes.npos);
dono = (no.find(c) != no.npos);
if (doyes || dono) break;
std::cout << "Type [" << yes << "] for yes, or [" << no << "] for no.";
std::cout << std::endl;
}
return doyes;
}
You can use it as others have suggested:
do {
// the interesting code
} while (again("Do you wish to continue? ", "y", "n"));

How about something like:
void DoYesCode()
{
// Do the code for the yes stuff...
}
...
do{
cout << "Do you wish to continue" << endl;
string answer;
getline(cin, answer);
if (answer=="y")
DoYesCode();
else if (answer=="n")
break;
else
cout << "That wasnt 'y' or 'n' please type something again" << endl;
} while(true);

Related

Using multiple conditions in an if statement in C++

I am trying to create a complex if statement in C++ that will save me from writing a whole bunch of if statements, I am wondering if this code below actually makes sense or I am overlooking an error.
if(input==choice) {
cout << "Tie!" << endl;
}else if(input=="rock" && choice=="scissors"
|| input=="scissors" && choice=="paper"
|| input="paper" && choice=="rock") {
cout << input " beats " << choice << ", you win!" << endl;
}else if(input=="rock" && choice=="paper"
|| input=="scissors" && choice=="rock"
|| input=="paper" && choice=="scissors"){
cout << choice << " beats " << input << ", you lose!" << endl;
}
What I am trying to achieve is:
"if input is x AND choice is y, OR if...."
Basically I'm testing multiple "if-and" conditions so that a single line of code will execute if it hits any of the if-and conditions. The output throws a "no match for 'operator||'" error.
You have a typo in input="paper" && choice=="rock"), but instead of fixing the typo I would suggest you to fix the code. No wonder you made a typo in this giant block of conditions. You have lots of repetion and mixing logic with output. If you spend some lines on includes, you can save some on code...
#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
bool win(const std::string& input, const std::string& choice) {
static const std::vector<std::pair<std::string, std::string>> wins =
{ { "rock", "scissors" },
{ "scissors", "paper" },
{ "paper", "rock" }
};
return std::find(wins.begin(), wins.end(), std::make_pair(input, choice))
!= wins.end();
}
int main() {
std::string choice = "paper";
std::string input = "scissors";
if (win(choice, input)) { std::cout << "you win! \n"; }
else { std::cout << "you lose! \n"; }
}
As next step you should eliminate all that strings, eg by using enums as discussed in the comments.

C++ Getting input from an external file

So I have this code here:
std::cout << "Here's Question 2 now for " << char(156) << "200" << endl;
Sleep(2000);
PlaySound(TEXT("Millionaire/£100Play.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
std::cout << "In maths, which of these numbers is not referred to as a square number?" << endl;
Sleep(2000);
std::cout << "A: 0" << endl;
Sleep(2000);
std::cout << "B: 1" << endl;
Sleep(2000);
std::cout << "C: 2" << endl;
Sleep(2000);
std::cout << "D: 4" << endl;
Sleep(2000);
answerQues2:
std::cout << "So, A, B, C or D?";
std::cin >> answer2;
if (answer2 == "C" || answer2 == "c")
{
std::cout << "That's correct, you've won " << char(156) << "200!" << endl;
PlaySound(TEXT("Millionaire/£100correct.wav"), NULL, SND_FILENAME);
Sleep(2000);
}
Now, the code itself is not the problem. What this is essentially is a quiz with a question and then 4 answers (A, B, C and D). Now in order to actually make up more questions, you'd have to go into the code itself and go through a lengthy process to edit everything. I want to make a text file that you can edit the questions and answers inside the text file, hence replacing everything in the code (So for example, if I wanted to change Q1, I could open the text file, replace the question and when I load up the program, the question will be changed). How would I be able to do this?
This is a full solution, though you must fill in the rest of your existing code. I personally use the below function, GetFileLines, to load lines from a file into a vector. Easy to work with that way. I took the time to adapt it to char/string since that is what you are using, though I default to wstring/wchar_t.
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <Windows.h>
using namespace std;
bool FileExists(const std::string& name) {
FILE * file;
errno_t result = fopen_s(&file, name.c_str(), "r");
if (result == static_cast<errno_t>(0)) {
fclose(file);
return true;
}
else {
return false;
}
}
std::vector<std::string> GetFileLines(std::string filePath)
{
vector<string> lines;
if (!FileExists(filePath))
return lines;
ifstream input(filePath);
if (!input.is_open() || input.fail())
return lines;
string line;
do {
std::getline(input, line);
lines.push_back(line);
} while (!input.eof() && !input.fail() && !input.bad());
if (!input.eof() && (input.fail() || input.bad()))
throw exception("GetFileLines failure");
return lines;
}
int wmain() {
vector<string> quizLines = GetFileLines("c:\\quiz.txt"); // replace with path to your file
if (quizLines.size() == 5) {
string question = quizLines[0];
string answer1 = quizLines[1];
string answer2 = quizLines[2];
string answer3 = quizLines[2];
string answer4 = quizLines[2];
// Your code begins here
std::cout << "Here's Question 2 now for " << char(156) << "200" << endl;
Sleep(2000);
PlaySound(TEXT("Millionaire/£100Play.wav"), NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
std::cout << question << endl;
Sleep(2000);
std::cout << "A: " << answer1 << endl;
// Rest of your code with changes to use answer# variables should follow
}
else {
std::cout << "Could not load quiz from external file. Cannot continue." << endl;
}
}
I recommend you read some documentation on this standard library elements I used that you are not familiar with. Any of these links, ordered by most common use first, may be useful to you for that:
http://www.cplusplus.com/reference/string/string/
http://www.cplusplus.com/reference/vector/vector/
http://www.cplusplus.com/reference/fstream/ifstream/
And don't pay attention to people down rating an honest question. Some were born into this world doing headstands it seems.
And also, for the record, this was an exceedingly ~easy~ question to answer. Why? Not because it was a dumb question, but because imagine how common it must be to try to access file contents. So if you ask a fundamental question like how do I get at that file content, you should expect a lot of quick full answers, because like in my case, they should be on hand. Could it be figured out using online searches, of course, though it's not always easy to figure out the pieces whose documentation you should be reading.

The best way to execute a sequence once in C++

Using C++ What could possibly the best way for a sequence to run only once? To make it clearer for example that I have a program in where you need to guess a string, if the user entered hint I would display the hint of the word but I would only allow it once... I am currently doing this:
bool hintLock = false;
...
if (guess == "hint"){
if (!hintLock){
cout << hint << endl;
hintLock = true;
}
else
cout << "I've given you the hint" << endl;
}
Here's my code:
#include <iostream>
#include <string>
using namespace std;
void main(){
string guess;
bool hintLock = false;
cout << "Guess one of StackExchange's best site: Type \"hint\" for hint" << endl << endl;
do{
cout << "Guess > ";
cin >> guess;
if (guess == "hint"){ // Here it is
if (!hintLock){
cout << hint << endl;
hintLock = true;
}
else
cout << "I've given you the hint" << endl;
}
}
while (guess != "stackoverflow");
cout << "You've got it right!" << endl;
}
Is there any much better statement to do this? or is it already the best?
I suspect that either you are overanalyzing things or you haven't adequately described the real problem. From the code you've posted, I see no reason why you shouldn't wrap the code you want to execute in to a function and then simply call that function one time.
void blahBlah()
{
// blah blah
}
int main()
{
if (userInput == "hint")
blahBlah();
}
Perhaps what you meant is that in your program you have a main loop which executes until program termination, and in that loop you accept input from the user. The user is allowed to ask for a hint, but only once during the program's run. The first time the ask for a hint they are given one, but subsequent times they are not.
I still believe simple is better than fancy (read: complex). To that end, I start with having a bool scoped outside of the main loop which you check each time they ask for help:
int main()
{
bool displayedHint = false;
// program's main loop
for (bool endProgram = false; !endProgram; )
{
std::string command = getUserInput();
if (command == "hint")
{
if (displayedHint)
{
cout << "I already gave you a hint!\n";
}
else
{
displayHint();
displayedHint = true;
}
}
}
}
If you want to make sure it only shows once for any particular run of the program (anything more global than that is pretty complicated and platfrom-specific), all you need is a global boolean to protect the function. For example:
bool shownHint = false;
void showHint() {
if (!shownHint) {
cout << "The hint is: ........" << endl;
shownHint = true;
} else {
cout << "Hint has already been shown, no cheating!" << endl;
}
Whenever you think you might want to show the hint, call showHint() and it will show (or not) as appropriate.
The trick is making sure the variable is global (or a static, in a class, which looks almost the same), so that it doesn't get reset during your loop or otherwise while the program is running.
If you need to persist between runs, you could write to a file, check a server, set a registry key, or any number of other options.
I think that the most appropriate iteration statement is do-while. It allows at least one iteration
for example
do
{
std::cout << "Enter your word: ";
std::string answer;
std::cin >> answer;
// some other stuff
} while ( false );
Or
bool allow = true;
do
{
std::cout << "Enter your word: ";
std::string answer;
std::cin >> answer;
// some other stuff
} while ( allow = !allow );
You can use flags that are boolean, that represent a state in your system. Once the state is "defined", you can then query the state and find if it is "set" or "cleared"..
to be more precise
bool hasHinted = false; // unset or cleared( also definition of a state variable)
if(hasHinted == false){
//do something
hasHinted = true; // set
}else{
// do nothing
}
I think the OP is looking for a piece of code similar to singleton init. i.e.: only create the singleton once, and after that always return the pointer.
void Init() {
static bool isInitialized = false;
if (!isInitialized) {
isInitialized = true;
doRealInit();
}
}
the same thing done with std::call_once as a comment suggests:
std::once_flag flag;
void do_once() {
std::call_once(flag, [](){ std::cout << "Called once" << std::endl; });
}
in my opinion it's not really more readable or shorter.

How to input a file into C++ and comparing console input

Im working on my homework assignment and I stuck because in the assignment we have to ask the user to enter a file name but also to type in either wc cc or lc (word count, character count, and line count of a file. For example, wc filename.txt. Im suppose to check the file to see if its valid or not which i understand and I know how to compare the users input to determine the different kind of function to run, but I dont understand how you could do it together. Any ideas? This is what I have so far.
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int main()
{
string line;
string file;
ifstream input; //input file stream
int i;
cout << "Enter a file name" << endl;
while(true){
cout << ">" ;
getline(cin,file);
input.open(file.c_str());
if (input.fail()) {
cerr << "ERROR: Failed to open file " << file << endl;
input.clear();
}
else {
i = 0;
while (getline(input, line))
if(line == "wc"){
cout << "The word count is: " << endl;
}
else if(line == "cc"){
cout << "The character count is: " << endl;
}
else if(line == "lc"){
cout << "The line count is: " << endl;
}
else if(line == "exit"){
return 0;
}
else{
cout << "----NOTE----" << endl;
cout << "Available Commands: " << endl;
cout <<"lc \"filename\"" << endl;
cout <<"cc \"filename\"" << endl;
cout <<"wc \"filename\"" << endl;
cout <<"exit" << endl;
}
}
}
return 0;
}
void wordCount(){
//TBD
}
void characterCount(){
//TBD
}
void lineCount(){
//TBD
}
You have to find the space between the command and the file name in the users input and then split the string where you find the space. Something like this
cout << "Enter a command\n";
string line;
getline(cin, line);
// get the position of the space as an index
size_t space_pos = line.find(' ');
if (space_pos == string::npos)
{
// user didn't enter a space, so error message and exit
cout << "illegal command\n";
exit(1);
}
// split the string at the first space
string cmd = line.substr(0, space_pos);
string file_name = line.substr(space_pos + 1);
This is untested code.
You could do better than this, for instance this would not work if the user entered two spaces between the command and the file name. But this kind of work rapidly gets very tedious. As this is an assignment I would be tempted to move on to more interesting things. You can always come back and improve things later if you have the time.
I think you are asking how to validate multiple arguments: the command and the file.
A simple strategy is to have function like the following:
#include <fstream> // Note: this is for ifstream below
bool argumentsInvalid(const string& command, const string & command) {
// Validate the command
// Note: Not ideal, just being short for demo
if("wc" != command && "cc" != command && "lc" != command) {
std::cout << "Invalid command" << std::endl;
return false;
}
// Validate the file
// Note: This is a cheat that uses the fact that if its valid, its open.
std::ifstream fileToRead(filename);
if(!fileToRead) {
std::cout << "Invalid file: \"" << filename << "\"" << std::endl;
return false;
}
return true;
// Note: This does rely on the ifstream destructor closing the file and would mean
// opening the file twice. Simple to show here, but not ideal real code.
}
If you want to evaluate ALL arguments before returning an error, insert a flag at the top of that function, like:
// To be set true if there is an error
bool errorFound = false;
and change all of the returns in the conditions to:
errorFound = true;
and the final return to:
return !errorFound;
Usage:
....
if(argumentsInvalid(command, filename)) {
std::cout << "Could not perform command. Skipping..." << std::endl;
// exit or continue or whatever
}
// Now do your work
Note: The specific validity tests here are over simplified.

ShouldI continue to use a while loop switch or do for a ask and answer program or should I use switch?

I am test out a question/answer program and relatively new to c++. I am trying to create a question/answer and so far using the while loop to check if the answer is true it will retuen the problem is if the question is wrong if the here is my code:
while(input1 == answer1)
{
cout << "Your answer is correct!\n";
answer_correct += 1;
break;
}
while(answer_correct = 0 )
{
cout << "Correct Answer is:\n" << answer1 << "\n";
break;
}
for ZETA
original code:
if (input3 == answer3)
{
cout << "Your answer is correct!";
answer_correct += 1;
}
else
{
cout << "Correct Answer is:\n" << answer3;
}
all if statements below this one are return false and display the answers below when input == to answer
Now I need to make it not show the correct answer using if statements. I am probably confused would someone educate me on a proper loop to use?
Is there any particular reason why you don't use a if-else statement?
if(input1 == answer1)
{
cout << "Your answer is correct!\n";
}
else
{
cout << "Correct Answer is:\n" << answer1 << "\n";
}
Explanation
The block/statement following the if(condition) will only be executed if and only if the condition is true. An else following an if will only be executed if and only if the preceding if failed.
See also:
CPP: Control Structures