First work on 'Quiz'
[lehre/programmieren-lernen.git] / Quiz / cpp / quiz.cpp
1 /*!
2 * \file quiz.cpp
3 * \brief Liest Fragen/Antworten aus einer Textdatei und lässt den Nutzer spielen.
4 *
5 * \author Andreas Bilke, M. Sc.
6 * \copyright GNU Public License
7 */
8
9 #include <cstdlib>
10
11 #include <algorithm>
12 #include <exception>
13 #include <fstream>
14 #include <iostream>
15 #include <stdexcept>
16 #include <string>
17 #include <vector>
18
19
20 class FrageAntworten {
21 private:
22 std::string frage;
23 std::vector<std::string> antworten;
24 unsigned int korrekteantwort;
25
26 public:
27 FrageAntworten(std::string pFrage, std::vector<std::string> pAntworten, int pKorrekteAntwort) : frage(pFrage), antworten(pAntworten), korrekteantwort(pKorrekteAntwort) { }
28
29 void stelleFrage() {
30 std::cout << "> " << frage << std::endl << std::endl;
31
32 for (size_t i = 0; i < antworten.size(); i++) {
33 std::cout << char(97 + i) << ") " << antworten[i] << std::endl;
34 }
35 }
36
37 bool liesAntwort() {
38 std::string zeilenpuffer;
39
40 std::cout << "Was ist die Antwort? ";
41 std::getline(std::cin, zeilenpuffer);
42
43 char antwort = zeilenpuffer[0];
44 unsigned int antwortNummer = antwort - 97 + 1;
45
46 if (antwortNummer == korrekteantwort) {
47 std::cout << "Antwort korrekt" << std::endl;
48 return true;
49 } else {
50 std::cout << "Antwort falsch" << std::endl;
51 return false;
52 }
53 }
54 };
55
56 std::vector<FrageAntworten> liesDatei(std::string& dateiname);
57 std::vector<std::string> teileZeile(std::string& zeile);
58
59 /*!
60 * \brief Einstiegspunkt für das Programm "quiz".
61 *
62 * \param argc Die Anzahl der Kommandozeilenparameter
63 * \param argv Die Kommandozeilenparameter
64 * \return Den Erfolgsstatus des Programms
65 */
66 int main(int argc, char** argv) {
67 if (argc != 2) {
68 std::cerr << "Benutzung: quiz <frageantwortdatei>" << std::endl;
69 return EXIT_FAILURE;
70 }
71
72 std::string quizdatei = std::string(argv[1]);
73 std::vector<FrageAntworten> frageantworten = liesDatei(quizdatei);
74
75 std::vector<size_t> fragenreihenfolge(frageantworten.size());
76 for (size_t i = 0; i < frageantworten.size(); i++) {
77 fragenreihenfolge[i] = i;
78 }
79 std::random_shuffle(fragenreihenfolge.begin(), fragenreihenfolge.end());
80
81 int korrekteAntworten = 0;
82 for (size_t i = 0; i < fragenreihenfolge.size(); i++) {
83 frageantworten[i].stelleFrage();
84 std::cout << std::endl;
85 bool korrekt = frageantworten[i].liesAntwort();
86 if (korrekt) {
87 korrekteAntworten++;
88 }
89 }
90
91 std::cout << "Das Spiel ist zu Ende. Du hattest " << korrekteAntworten << " korrekte Antworten." << std::endl;
92
93 return EXIT_SUCCESS;
94 }
95
96 std::vector<FrageAntworten> liesDatei(std::string& dateiname) {
97 std::ifstream dateistream(dateiname);
98 if (!dateistream.good()) {
99 throw std::ios_base::failure("Kann Datei nicht lesen.");
100 }
101
102 std::vector<FrageAntworten> frageantworten;
103
104 std::string zeile;
105 while (std::getline(dateistream, zeile).good()) {
106 std::vector<std::string> getrennteElemente = teileZeile(zeile);
107 if (getrennteElemente.size() < 3) {
108 throw std::logic_error("Das Dateiformat stimmt nicht. Es muss pro Zeile Eine Frage, eine korrekte Antwort und mindestens eine Antwortmöglichkeit geben.");
109 }
110
111 std::reverse(getrennteElemente.begin(), getrennteElemente.end());
112
113 std::string frage = getrennteElemente.back();
114 getrennteElemente.pop_back();
115 unsigned int korrekteantwort = std::stoi(getrennteElemente.back());
116 getrennteElemente.pop_back();
117
118 if (korrekteantwort < 0 || korrekteantwort >= getrennteElemente.size()) {
119 throw std::logic_error("Das Dateiformat stimmt nicht. Die Angabe der korrekten Antwort referenziert auf eine nicht existierende Antwortmöglichkeit in der Datei.");
120 }
121
122 std::reverse(getrennteElemente.begin(), getrennteElemente.end());
123 FrageAntworten frageantwort = FrageAntworten(frage, getrennteElemente, korrekteantwort);
124
125 frageantworten.push_back(frageantwort);
126 }
127
128 return frageantworten;
129 }
130
131 std::vector<std::string> teileZeile(std::string& zeile) {
132 std::string::size_type letztePos = zeile.find_first_not_of(";", 0);
133 std::string::size_type pos = zeile.find_first_of(";", letztePos);
134
135 std::vector<std::string> getrennteElemente;
136
137 while (std::string::npos != pos || std::string::npos != letztePos) {
138 getrennteElemente.push_back(zeile.substr(letztePos, pos - letztePos));
139 letztePos = zeile.find_first_not_of(";", pos);
140 pos = zeile.find_first_of(";" , letztePos);
141 }
142
143 return getrennteElemente;
144 }