0

I wrote a code which shows a question and 4 answers, ask to choose the correct one, and checks the choice. Now I would build a table which would contain 6 columns: question/a/b/c/d/correct_answer, and about hundred of lines with my questions. Now I would like my program to randomly choose 5 of a question from table and show it to user.

First question is, can I do my table in Excel and use it than in Visual Studio? If not, what software should I use to do table like this as simply as possible, and how to implement it into my Visual studio project? If yes, how to implement Excel table to my Visual studio project?

Second question is, what simplest code should I write to randomly choose 5 of 100 question from my table?

A code of a question is:

int main()
{

    string ans; //Users input
    string cans; //Correct answer, comes from table
    string quest; //Question, comes from table
    string a; // Answers, come from table
    string b;
    string c;
    string d;
    int points; //Amount of points

    points = 0;

    cout << "\n" << quest << "\n" << a << "\n" << b << "\n" << c << "\n" << d << "\n";
    cout << "Your answer is: ";
    cin >> ans;
    if (ans == cans)
    {
        points = points + 1;
        cout << "Yes, it is correct\n";
    }
    else
    {
        cout << "No, correct answer is " << cans << "\n";
    }


    return 0;
}
Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120
Mac
  • 11
  • easiest would be to load data from a text file, one line per question, you should use a data structure that supports indexing by number if you need to fetch random records, hope that was helpful – mike morris Dec 31 '16 at 02:40

2 Answers2

0

First Part

You can definitely use Excel to edit your questions and save them. But when you save it, pay attention to the file format.

You want to save your Excel file as a .csv file instead of .xls or .xlsx file. Just go to File -> Save File As -> and change type to .csv.

This is because, reading .csv files is a lot easier. .csv files have each cell in row separated by a comma (,) and each row by a newline ('\n') character.

So, here is a sample Excel file:

Sample Data in Excel format

After I save it as a .csv file and open it using a text editor (Atom, here), it looks like this:

Sample Data in .csv format

After, that you only need to write some code to read the file.

This is the code I used (I've added excessive comments to make the code more explicit for beginners):

#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>

using namespace std;
const int MAX_QUESTIONS = 3;
const int COLUMNS = 6; //Question, Options A, B, C, D, Correct Answer

int main() {

    ifstream fp;
    fp.open("test.csv");

    //If file could not be opened
    if (fp.fail()) {
        std::cout << "failed to open file" << std::endl;
        return 1;
    }

    //Create a 2D vector of strings to store the values in the file
    vector< vector<string> > table;

    string line;

    //Loop through the entire file (fp)
    //Store all values found until I hit a newline character ('\n') in the string line
    //This loop automatically exits when the end-of-file is encountered
    while (getline(fp, line, '\n')) {

        //Create an temporary vector of strings to simulate a row in the excel file
        vector<string> row;

        //Now I am passing in this string into a string stream to further parse it
        stringstream ss;
        ss << line;

        string part;

        //Similar to the outer loop, I am storing each value until I hit a comma into the string part
        while (getline(ss, part, ',')) {

            //Add this to the row
            row.push_back(part);
        }

        //Add this row to the table
        table.push_back(row);
    }

    //Print out the entire table to make sure your values are right
    for (int i = 0; i <= MAX_QUESTIONS; ++i) {
        for (int j = 0; j < COLUMNS; ++j) {
            cout << table[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}

Second Part

To choose a random number, you can use this code (I borrowed it from another answer)

#include <random>

std::random_device rd;     // only used once to initialise (seed) engine
std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
std::uniform_int_distribution<int> uni(min,max); // guaranteed unbiased

auto random_integer = uni(rng);

Unlike the old method, this doesn't create bias towards the lower end. However, the new engine is available only in C++11 compilers. So keep that in mind. If you need to use the old method, you can correct the bias by following this answer.

To choose 5 different numbers, each time you generate a random number store it in an array and check whether this number has already been used. This can prevent repetition of questions.

Community
  • 1
  • 1
Apara
  • 223
  • 4
  • 12
  • Ok, thanks for your reply. I followed your steps and my program prints my table very well. – Mac Dec 31 '16 at 14:04
  • But how to attach parts of every line (questions, answer a, answer b, etc) to my strings decleared at the beginning? – Mac Dec 31 '16 at 14:05
  • Well, I guess I found a solution, but you can post yours to compare. – Mac Dec 31 '16 at 14:36
  • @Mac what do you mean by attaching parts of every line to the string? You can access the question, options and answer, after storing them in the 2D vector, just like you would access a 2D array. For example, if you want to retrieve the 1st question, you would say `string quest = table[1][0];`. Does that answer your question? And if you found the answer helpful, accept it :) – Apara Dec 31 '16 at 22:01
  • That is, what I discovered. Thanks so much. My question might look silly but it is my second day in c++. But my project is almost finished and it works so far. – Mac Dec 31 '16 at 23:33
  • and the last question, I hope. Is there any magic code to check how many rows doest the table contain? When I use 'i = rand() % maxq + 1;' maxq is an int that means amount of questions but it comes frome cin, and it is an users input. I would prefer to check maxq by the program. – Mac Jan 01 '17 at 13:04
  • @Mac to get the number of rows in a 2D vector, you use the `size()` function. So `num_rows = table.size();` Each row can have different number of columns as this is a 2D vector, so to get the number of columns in the `i`th row, `columns_i = table[i].size();`. However, the number of columns in all rows are the same in your case. So it shouldn't matter what your `i` is as long it is within the range. – Apara Jan 01 '17 at 19:12
0

To answer question 1: In Excel, click File>Save As. Choose UTF-16 Unicode Text (.txt), name your file, and save it where your program can access it. In your C++ program, use the fstream library for text files:

#include <fstream>

With the text file in the same directory, use

ifstream fin;
fin.open("FILENAME.txt");
.
.
.
fin >> variable;
.
.
.
fin.close();

To answer question 2: There is a rand() function that returns an integer between zero and RAND_MAX. Then you can use modulus(%) to get a random number in the desired range.

#include <cstdlib> // for srand() and rand()
#include <ctime> // for time()
.
.
.
srand(time(NULL)); // seed the RNG on seconds
.
.
.
var = rand() % ONE_MORE_THAN_MAX;

Here's a quizzer I made to help myself study:

#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
#include <ctime>
using namespace std;

const char QUESTIONS_FILE_NAME[] = "questions.dat";
const char QUESTION_ANSWER_SEPARATOR = ',';
const char QUESTION_SEPARATOR = '\n';

const short MAX_QUESTION_LENGTH = 300;
const short MAX_ANSWER_LENGTH = 300;
const short MAX_NUM_QUESTIONS = 300;


int main()
{
  char questions[MAX_NUM_QUESTIONS][MAX_QUESTION_LENGTH];
  char answers[MAX_NUM_QUESTIONS][MAX_ANSWER_LENGTH];
  short counter = 0;

  short question_choice;

  char user_answer[MAX_ANSWER_LENGTH];

  ifstream fin;
  fin.open( QUESTIONS_FILE_NAME );


  srand(time(NULL));


  while(fin.getline( questions[counter], MAX_QUESTION_LENGTH-1, 
    QUESTION_ANSWER_SEPARATOR ))
  {
    fin.getline( answers[counter], MAX_ANSWER_LENGTH-1,
      QUESTION_SEPARATOR );
    counter++;
  }

  fin.close();

  cout << endl << "Press CTRL+C to quit at any time" << endl << endl;

  while ( counter > 0 )
  {
    question_choice = rand() % counter;

    cout << endl << questions[question_choice] << " ";
    cin >> user_answer;

    if ( strcmp( user_answer, answers[question_choice] ) )
    {
      cout << endl << "Incorrect" << endl 
        << "It was " << answers[question_choice] << endl;
      strcpy( questions[counter], questions[question_choice] );
      strcpy( answers[counter], answers[question_choice] );
      counter++;
    }
    else
    {
      cout << endl << "Correct" << endl;
      counter--;
      strcpy( questions[question_choice], questions[counter] );
      strcpy( answers[question_choice], answers[counter] );
    }  
  }


  cout << endl << "You Win!!!!" << endl;

  return 0;
}

questions.dat would have data like this

In what year was the Pendleton Civil Service Reform Act enacted?
a.1873
b.1883
c.1893
d.1903,b
In what year did Hurricane Katrina occur?
a.2001
b.2003
c.2005
d.2007,c
  • The rand() function creates a bias towards the lower end. And this is very much visible when dealing with even a range as small as (1, 100). So unless, you are forced to not use C++11 compiler, you can use the method outlined [here](http://stackoverflow.com/a/19728404/5055644). And why not use `string`s instead of `char`s? You don't have to worry about the length of the strings (or am I wrong in assuming that ?) – Apara Dec 31 '16 at 03:00