C++ user entry and error handling - c++

I am currently writing a knights tour program and I am trying to think of the best way to handle the problem I am running into. My goal is to prompt the user to enter in several inputs for a starting place on the board. This means that the user needs to be able to type in several numbers which in my opinion, this means I need an empty array that can accept the input. I am coding in C++ and this is what I have so far:
#include <iostream>
#include <iomanip>
using namespace std;
int greeting(){
cout << "Welcome to the Knights Tour! Please, enter your desired starting places." <<
endl << "Enter any numbers 1-64, and type -1 to start." << endl << endl;
int board[8][8] = {{1,2,3,4,5,6,7,8},
{9,10,11,12,13,14,15,16},
{17,18,19,20,21,22,23,24},
{25,26,27,28,29,30,31,32},
{33,34,35,36,37,38,39,40},
{41,42,43,44,45,46,47,48},
{49,50,51,52,53,54,55,56},
{57,58,59,60,61,62,63,64}
};
for (int row = 0; row < 8; row++){
for(int column=0; column<8; column++){
cout << setw(3) << board[row][column] << " ";
}
cout << endl;
}
cout << endl;
}
int main(){
greeting();
}
I have tried several methods to accept the user input but what I am ultimately trying to do is allow only integer input from 1-64 and allow the user to type -1 to exit the loop.
Thanks for the help!

int position = 0;
std::string line;
do
{
while (std::cout << "Select position 1-64, or type -1 to exit\n" &&
std::getline(std::cin, line))
{
std::istringstream iss(line);
position = 0;
if (iss >> position &&
(position == -1 ||
position 1 <= position && position <= 64))
{
char c;
if (!(iss >> c))
break;
std::cerr << "trailing character also found on input line, please try again\n";
}
else
std::cerr << "you entered an invalid position, please try again\n";
}
if (1 <= position && position <= 64)
{
// do/call your position-related work here...
}
} while (position != -1 && std::cin);

Related

How to Accept [ENTER] key as an invalid input and send out error message

This is a program that grade user inputs for the questions of Driver's License Exam.
I'm having trouble of validating the user input.
I'd like to accept the [ENTER] key as an invalid input and proceed to my validation rather than just go to an empty line and cannot process to the next question. Purpose is to send out error message and that no input is given and [ENTER] key is not valid input and only accept one more chance to enter valid input which are a/A, b/B, c/C, or d/D. So that is why I'm using if statement here instead of loop.
I tried if (testTakerAnswers[ans] == (or =) '\n') {} but still doesn't solve the problem of newline.
I include curses.h in here hope to use getch() statement from the other post but somehow I can't manage to work in my code with an array instead of regular input.
I'm looking for other methods as well rather than getch()
So should I adjust my bool function, or directly validate input in main() function.
#include <iostream>
#include <iomanip>
#include <string>
#include <cctype>
#include <curses.h>
using namespace std;
const unsigned SIZE = 20; // Number of qns in the test
char testTakerAnswers[SIZE]; //Array to hold test taker's answers
bool validateInput(char);
class TestGrader
{
private:
char answers[SIZE]; // Holds the correct answers // Answer is array
int getNumWrong (char[]);
void missedQuestions (char[]);
public:
void setKey(string); // Initialize object with standard keys
void grade(char[]); // Grades the answers from tester
};
void TestGrader::setKey(string key){
if (key.length()!=SIZE){
cout << "Error in key data.\n";
return;
}
for (unsigned pos = 0; pos < SIZE ; pos ++)
answers [pos] = key [pos];
}
void TestGrader::grade(char test[])
{
int numWrong = getNumWrong(test);
if (numWrong <= 5)
cout << "Congratulations. You passed the exam.\n";
else
cout << "You did not pass the exam. \n";
cout << "You got " << (SIZE-numWrong) << " questions correct. \n";
if (numWrong > 0){
cout << "You missed the following " << numWrong << " questions: \n";
missedQuestions(test);
}
}
int TestGrader::getNumWrong(char test[])
{
int counter = 0;
for (int i = 0; i < SIZE; i++){
if (answers[i] != toupper(testTakerAnswers[i])){
counter++;
}
}
return counter;
}
void TestGrader::missedQuestions(char test[])
{
// cout << testTakerAnswers[i]; This is to print taker's answers
int counter = 0;
for (int i = 0; i < SIZE; i++){
if (answers[i] != toupper(testTakerAnswers[i])){
cout << "\n" << i + 1 << ". Correct answers: " << answers[i];
counter++;
}
}
}
bool validateInput(char ans){ // Only A, B, C, D valid input
if (toupper(ans)!='A' && toupper(ans)!= 'B' && toupper(ans)!='C' && toupper(ans)!= 'D'){
cout << "\n********************WARNING*******************\n";
cout << "Invalid input! Enter only a/A, b/B, c/C, or d/D\n";
return false;
}
if (testTakerAnswers[ans] == '\n'){
return false;
}
return true;
}
int main()
{
const int NUM_QUESTIONS = 20;
string name; //Test taker's name
char doAnother; //Control variable for main processing loop
TestGrader DMVexam; //Create a TestGrader object
DMVexam.setKey("BDAACABACDBCDADCCBDA");
do {
cout << "Applicant Name: ";
getline(cin,name);
cout << "Enter answer for " << name << ".\n";
cout << "Use only letters a/A, b/B, c/C, and d/D. \n\n";
for (int i = 0; i < NUM_QUESTIONS; i++){
// Input and validate it
do{
cout << "Q" << i+1 << ": ";
cin >> testTakerAnswers[i];
if (!validateInput(testTakerAnswers[i])){
cout << "You get one more chance to correct.\nOtherwise, it count as wrong answer.";
cout << "\n*********************************************";
cout << "\nRe-enter: ";
cin >> testTakerAnswers[i];
cout << '\n';
break;
}
}while(!validateInput(testTakerAnswers[i]));
}
//Call class function to grade the exam
cout << "Results for " << name << '\n';
DMVexam.grade(testTakerAnswers);
cout << "\nGrade another exam (Y/N)? ";
cin >> doAnother;
while (doAnother != 'Y' && doAnother != 'N' && doAnother != 'y' && doAnother != 'n'){
cout << doAnother << " is not a valid option. Try Again y/Y or n/N" << endl;
cin >> doAnother;}
cout << endl;
cin.ignore();
}while(doAnother != 'N' && doAnother != 'n');
return 0;
}
Your issue is cin >> testTakerAnswers[i]; cin is whitespace delimited, that means that any whitespace (including '\n') will be discarded. So testTakerAnswers[i] can never be '\n'.
I'm not sure exactly what you want to do, but possibly try
getline(cin,input_string);
then
input_string == "A" | input_string == "B" | ...
So if only the enter key is pressed, input_string will become "".

strange behaviour while pressing enter when recieving input with conditions with cin

I have this code that checks if index is an integer between 1 and the size of a vector member called options_ (menu implementation):
int ConsoleMenu::GetSelection() {
int index;
std::cout << "Please enter your selection index. " << std::endl;
while (!(std::cin >> index) || std::cin.get() != '\n' || index < 1 || index > options_.size()) {
std::cout << "Error. index must be a valid integer. Try again: " << std::endl;
std::cin.clear();
std::cin.ignore(256, '\n');
}
}
but sometimes when I input a number and press enter it seems as if the program does not recognize I pressed enter. Can someone please help?
Thanks a lot!
Read a line with getline(), parse it using a std::stringstream, and test if it meets your criteria:
int ConsoleMenu::GetSelection() {
std::cout << "Please enter your selection index. " << std::endl;
while (true) {
std::string line;
std::getline(std::cin, line);
std::stringstream linest(line);
int index;
if ((linest >> index) && index >= 1 && index < options_.size()) {
return index;
}
std::cout << "Error. index must be a valid integer. Try again: " << std::endl;
}
}

How do I use cin in a while loop?

I'm trying to get the user to input their name(s) using a while loop with an array and cin, but after the last person's name is input, the program crashes instead of moving on. Is there a way to fix this, or do I need to completely change up the code? I'm also fairly new to c++, so can any answers be given as simply as possible?
#include <iostream>
#include <string>
using namespace std;
int main()
{
unsigned int numberofplayers;
number://loop back here if more than 4 players
cout << "Number of players: ";
cin >> numberofplayers;
if(numberofplayers > 4 || numberofplayers < 1){
cout << "Invalid number, please enter a number from 1 to 4." << endl;
goto number;
}
string name[numberofplayers];
cout << "Enter your name" << endl;
int a = 1;
while(a < numberofplayers + 1){
cout << "Player " << a << ": ";
cin >> name[a];
cout << "Hello, " << name[a] << "." << endl;
a++;
}
}
You would probably facing array index out of bound, so Change you while loop to this and set a=0 to fill from 0th index.
while(a < numberofplayers){
}
Your last iteration exceeds the size of the array. You need to change it to
while(a < numberofplayers)
also, on another note, the keyword goto isn't used much anymore. I would suggest using a while there also like
while(true){
cout<<"number of players";
cin>>numberofplayers
if(numberofplayers is valid input){
break;
}
cout<<"bad input";
}
There is a question on stackoverflow discussing the use of goto extensively here:
GOTO still considered harmful?

Sudoku input program skipping a prompt

I wrote a program for my computer science class that validates and solves sudoku puzzles from .txt files, but I wanted to take it one step further and write a program that made it easy to input and sudoku game. I'm sure you can figure out the format of the files based on this code. My only problem is that the last cin gets skipped, and that option is important to me. Any insight will be appreciated!!
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct s {
s();
~s() {/*zzzz*/}
void show_grid();
void set (int &r, int &c, int &v) {g[r][c] = v;}
private:
int g[9][9];
};
//************************************************************************
void s::show_grid() {
//print game out to check it
cout << " | ------------------------------- |" << endl;
for (int k=0; k<81; k++) {
if (k%3 == 0)
cout << " |";
cout << " " << g[k/9][k%9];
if (k%9 == 8) {
cout << " |" << endl;
if ((k/9)%3 == 2)
cout << " | ------------------------------- |" << endl;
}
}
cout << endl;
}
//************************************************************************
s::s() {
//initialize all elements to zero
for (int i=0; i<9; i++) {
for (int j=0; j<9; j++) {
g[i][j] = 0;
}
}
}
//************************************************************************
void create_name (string &name) {
//append .txt extension LIKE IT OR NOT
string ext = name;
ext.erase(ext.begin(), ext.end() - 4);
if (ext.compare(".txt")!=0)
name.append(".txt");
}
//************************************************************************
int main () {
s g;
string name;
string yon("");
int count = 0;
int row, col, val, rcv;
ofstream os;
cout << "Enter game file name: ";
cin >> name;
create_name(name);
//open and do typical checks
os.open(name.c_str());
if (os.fail()) {
cerr << "Could not create " << name << ". Waaaah waaaaaaaaaah...\n\n";
return 0;
}
//useful output (hopefully)
cout << "Enter grid coordinates and value as a 3-digit number,\n"
<< "from left to right, row by row.\n"
<< "(e.g. 2 in first box would be 112)\n";
//take input as one int, to be user friendly
while (cin >> rcv && count < 81) {
row = (rcv / 100) - 1;
col = ((rcv / 10) % 10) - 1;
val = rcv % 10;
os << row << " " << col << " " << val << endl;
g.set (row, col, val);
count++;
}
os.close();
//From here down is broken, but it still compiles, runs, and works
cout << "Show grid input(y/n)?\n";
cin >> yon;
if (yon.compare("y")==0)
g.show_grid();
else if (yon.compare("n")==0)
cout << "Peace!\n";
return 0;
}
The problem is here:
while (cin >> rcv && count < 81)
Consider what happens when count==81: First, rcv will be input from cin, and only then the condition count < 81 will be evaluated to false. The loop will stop, and the value of rcv will be ignored. So effectively you read one input too many.
You should change the order of evaluation, so that count is checked first:
while (count < 81 && cin >> rcv)
Edit:
According to your comment above you are actually expecting to read less than 81 values. In that case, I recommend having the user input a special value (for example, 0) to terminate the loop. You'd just need to add if (rcv==0) break;. If you just input an invalid value as you are apparently doing, the cin stream will be put in a failed state and further input will not succeed.
Try something like:
//useful output (hopefully)
cout << "Enter grid coordinates and value as a 3-digit number,\n"
<< "from left to right, row by row.\n"
<< "(e.g. 2 in first box would be 112)\n"
<< "or Z to end the loop\n"; // 1
//take input as one int, to be user friendly
while (count < 81 && cin >> rcv ) { // 2
row = (rcv / 100) - 1;
col = ((rcv / 10) % 10) - 1;
val = rcv % 10;
os << row << " " << col << " " << val << endl;
g.set (row, col, val);
count++;
}
if(!std::cin) { // 3
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
1) Let the user know that he can enter in invalid char. It doesn't have to be Z, actually any non-numeric char will work.
2) Fix off-by-one error in the order of the &&.
3) If std::cin is in error state, clear the error and ignore the Z.
cin >> yon
still actually reads in a variable, it just reads in the variable that the while loop found to be false. When the while loop condition returns false rcv is ignored, so the number remains in the input stream waiting for the next cin statement. When yon is called that number meant for rcv is read into yon, giving you some strange errors.
it would be better to use interjay's method:
while (count < 81 && cin >> rcv)

vector-related segmentation fault

void offer_help();
bool play_one_game();
int main() {
offer_help();
play_one_game();
}
void offer_help() {
int help_response;
cout << "Need help? (0/1) ";
cin >> help_response;
if (help_response == 1)
cout << "I will generate a pattern of 4 numbers, each in the range 0 through 9.\n Each guess that you enter will be a line containing 4 integers,\n separated by spaces, such as:\n\t 2 4 7 1\n FOr each guess, I will echo back a lost consisting of\n 0's and 1's, with a 1 in a given position meaning that\n you guessed the number, and a zero meaning that you didn't.\n For example, if the actual solution was 2 3 6 1, I'll respond\n\t 1 0 0 1\n See how many guesses it takes you to get the solution!\n\n If you want to give up, type a negative number for one of\n your guesses, and we'll tell you what the pattern was.\n\n";
}
bool play_one_game() {
srand(time(0)); //needed to start randint
vector<int> solution; //vector of 4 randomly generated
//solutions
vector<int> guess; //vector containing user guesses.
vector<int> result;
int guess_input;
for(int i = 0; i < solution.size(); ++i)
solution[i] = randint(10);
int trial_number = 0; //int that shows what guess the user is on
while (play_one_game() == true) {
//ask user for inputs.
cout << "Guess #" << ++trial_number << "? ";
for (int i = 0; i < guess.size(); ++i){
cin >> guess_input;
guess.push_back(guess_input);
}
//outputs error if user inputs a letter.
if (!cin) {
cerr << "Bad input data! Feed me numbers!\n";
return 43;
}
if (cin < 0){
cout << "Too bad! Solution was " << endl;
for(int i = 0; i < result.size(); i++)
cout << (result[i]);
}
//determines if user correctly guessed any of the
//numbers and tells the user which is correct.
for (int i = 0; i < result.size(); i++) {
if (guess[i]==solution[i])
cout << 1 << " ";
else if (guess[i]!=solution[i])
cout << 0 << " ";
}
cout << endl;
// playagain();
cout << endl << "Play again (0/1)? ";
int replay;
cin >> replay;
if (replay == 0) {
play_one_game() == false;
return 5;
}
else if (replay == 1)
play_one_game() == true;
else {
cerr << "wat?\n";
return 10;
}
}
}
This is designed to allow a player to guess a pattern of random numbers.
No idea why I am getting a segmentation fault. The program is supposed to call the offer_help function, then the play_one_game function within main function. Then it should ask the player whether he wants to play again. If no, then bool play_one_game should be set to false and it should exit.
This is related to the play_one_game bool function.
You're getting a segmentation fault, because you end up in an endless recursion in the following line:
while (play_one_game() == true) {
play_one_game will call play_one_game in this line, and this will call play_one_game in the same line again. This will result in a stack overflow at last.
Better use some bool keepPlaying; and while(keepPlaying) instead.
EDIT: Well, this is a little bit more than a simple answer, but I like games, so... have a look at the following code:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
bool play_one_game();
void offer_help() {
int help_response;
std::cout << "Need help? (0/1) ";
std::cin >> help_response;
if (help_response == 1)
std::cout << "I will generate a pattern of 4 numbers, each in the range 0 through 9.\n"
"Each guess that you enter will be a line containing 4 integers,\n"
"separated by spaces, such as:\n"
"\t 2 4 7 1\n"
"For each guess, I will echo back a lost consisting of\n"
"0's and 1's, with a 1 in a given position meaning that\n"
"you guessed the number, and a zero meaning that you didn't.\n"
"For example, if the actual solution was 2 3 6 1, I'll respond\n"
"\t 1 0 0 1\n"
"See how many guesses it takes you to get the solution!\n\n"
"If you want to give up, type a negative number for one of\n"
"your guesses, and we'll tell you what the pattern was.\n\n";
}
int main() {
offer_help();
srand(time(0)); // Initialize random numbers with current time as seed
while(play_one_game()); // if play_one_game returns true, play again
}
bool play_one_game() {
std::vector<int> solution(4); // Four solutions for our guessing game
std::vector<int> guess; // User guesses
for(unsigned i = 0; i < solution.size(); ++i)
solution[i] = rand() % 10;
int trial_number = 0; //int that shows what guess the user is on
bool keepPlaying = true;
while(keepPlaying){
std::cout << "Guess #" << ++trial_number << "? ";
guess.clear(); // Clear old guesses
for(unsigned i = 0; i < solution.size(); ++i){
int guess_input;
//outputs error if user inputs a letter.
if (!(std::cin >> guess_input)) {
std::cerr << "Bad input data! Feed me numbers!\n";
std::cerr << "Try again!" << std::endl;
std::cin.clear(); // Clear flags
continue;
}
if (guess_input < 0){
std::cout << "Too bad! Solution was " << std::endl;
for(unsigned i = 0; i < solution.size(); i++)
std::cout << (solution[i]);
keepPlaying = false;
break;
}else
guess.push_back(guess_input);
}
if(!keepPlaying)
break;
if(solution.size() != guess.size()){
std::cerr << "Wrong number of guesses, try again!" << std::endl;
continue;
}
//determines if user correctly guessed any of the
//numbers and tells the user which is correct.
bool correct = true;
for (unsigned i = 0; i < solution.size(); i++) {
if (guess[i] == solution[i])
std::cout << 1 << " ";
else{
correct = false;
std::cout << 0 << " ";
}
}
if(correct){
std::cout << "Congratulations - you won!" << std::endl;
break;
}
std::cout << std::endl;
}
int replay = -1;
do{
// Ask user for input until input is 0 or 1
std::cout << std::endl << "Play again (0/1)? ";
std::cin >> replay;
}
while(replay != 0 && replay != 1);
return static_cast<bool>(replay); // return user replay answer (false/true)
}
Try to keep your code as simple as possible. Welcome to SO. And don't expect future answers to be that excessive.
You're never inserting anything into your solution vector. You just declare the vector, and then say:
for(int i = 0; i < solution.size(); ++i)
solution[i] = randint(10);
...which won't do anything since at this point solution.size() == 0. Later, when you iterate over your result vector, you end up accessing invalid elements in your empty solution vector. You also can't assume that the result vector and solution vector are the same size.