x017.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "x011.h"
4 #include <random>
5 
6 namespace nmx::apps::x017 {
7 
9 
15 template<class T>
17 {
18 public:
19  enum class Ops { plus, minus, mul, div };
20  //Synonym: Speicher für Zahlen
21  using DataC = std::vector<T>;
22  //Synonym: Speicher für mathematische Operationen
23  using OpsC = std::array<Ops, 4>;
24  //Synonym: Testfrage
25  using Question = std::tuple<T, T, Ops>;
26 
27 private:
28  //rationale Zahlen
29  DataC _data;
30  //mathematische Operationen
31  OpsC _types;
32  //Anzahl der Fragen in einem Test
33  size_t _maxQuestions;
34  size_t _eIdx, _tIdx;
35 
39  void shuffle_data() {
40  //erzeuge Startwert für den Zufallsgenerator
41  std::random_device rd;
42 
43  //Pseudo-Zufallsgenerator
44  std::default_random_engine g(rd());
45  std::shuffle(_data.begin(), _data.end(), g);
46 
47  //Index hat ungültigen Wert!
48  _eIdx = _data.size();
49  }
50 
55  auto next_item() {
56  //wenn der Index einen ungültigen Wert hat erhält er den Wert 0
57  // sonst rückt er eine Position weiter
58  _eIdx = (_eIdx == _data.size()) ? 0 : _eIdx + 1;
59  return _data[_eIdx];
60  }
61 
65  void shuffle_ops() {
66  //erzeuge Startwert für den Zufallsgenerator
67  std::random_device rd;
68 
69  //Pseudo-Zufallsgenerator
70  std::default_random_engine g(rd());
71  std::shuffle(_types.begin(), _types.end(), g);
72  _tIdx = _types.size();
73  }
74 
79  auto next_op() {
80  //bei ungültigem Wert setze den Wert auf 0
81  _tIdx = (_tIdx == _types.size()) ? 0 : _tIdx + 1;
82  return _types[_tIdx];
83  }
84 
85 public:
92  TestGenerator(size_t maxq, const DataC &data) {
93  _data = data; //kopiere Zahlen
94  shuffle_data(); //mische Container mit Zahlen
95 
96  //Anzahl der Fragen im Test (Startwert).
97  //Wird nach einer neu gestellten Frage um 1 reduziert
98  _maxQuestions = maxq;
99 
100  //initialisiere Container mit Operatoren
101  _types = { Ops::plus, Ops::minus, Ops::mul, Ops::div };
102 
103  //mische Container mit Operatoren
104  shuffle_ops();
105  }
106 
111  auto next_question() {
112  //Anzahl der noch zu stellenden Fragen
113  _maxQuestions--;
114  //generiere Frage
115  return std::make_tuple(next_item(), next_item(), next_op());
116  }
117 
122  bool has_questions() { return _maxQuestions != 0; }
123 
129  static T calc_result(const Question &q) {
130  //lese erste Zahl
131  auto r1 = std::get<0>(q);
132  //lese zweite Zahl
133  auto r2 = std::get<1>(q);
134  //das Ergebnis
135  T r;
136  //welcher Operator ?
137  switch (std::get<2>(q)) {
138  case Ops::plus: {
139  r = r1 + r2;
140  } break;
141  case Ops::minus: {
142  r = r1 - r2;
143  } break;
144  case Ops::mul: {
145  r = r1 * r2;
146  } break;
147  case Ops::div: {
148  r = r1 / r2;
149  } break;
150  }
151  return r;
152  }
153 }; //TestGenerator
154 
159 class TestUI
160 {
161 public:
164 
165  //Testgenerator
167  //algebraische Operationen
168  static const std::array<std::string, 4> ops;
169  //Anzahl der richtigen Anttworten
170  size_t _score = 0;
171  //maximale Punktzahl
172  const size_t _maxScore;
173 
174 public:
180  TestUI(size_t maxq, const Test::DataC &data)
181  : test{ maxq, data }
182  , _maxScore{ maxq } {}
183 
187  void run() {
188  while (test.has_questions()) {
189  try {
190  std::string answer;
191  //generiere Frage
192  const auto q = test.next_question();
193  //zeige Frage auf dem Bildschirm
194  std::cout << "q: ";
195  std::cout << to_string(q) << "=?" << std::endl;
196  //lese Antwort als Zeichenkette
197  std::cout << "a:";
198  getline(std::cin, answer);
199  //berechne korrekte Antwort
200  auto cresult = Test::calc_result(q);
201  auto uresult = read(answer);
202  if (cresult == uresult) {
203  std::cout << "OK" << std::endl;
204  _score++;
205  } else {
206  std::cout << "correct answer is:" //
207  << cresult << std::endl;
208  }
209  } catch (const std::out_of_range &err) {
210  std::cout << "wrong input format" << std::endl;
211  std::cout << "input must be like a/b" << std::endl;
212  std::cout << err.what() << std::endl;
213  } catch (const std::invalid_argument &err) {
214  std::cout << "wrong input format" << std::endl;
215  std::cout << "input must be like a/b" << std::endl;
216  std::cout << err.what() << std::endl;
217  } catch (const std::runtime_error &err) {
218  std::cout << "wrong input format" << std::endl;
219  std::cout << "input must be like a/b" << std::endl;
220  std::cout << err.what() << std::endl;
221  }
222  }
223 
224  const auto score = static_cast<double>(_score) / static_cast<double>(_maxScore) * 100;
225  std::cout << "score:" << score << "%" << std::endl;
226  }
232  inline static std::string to_string(const Test::Question &q) {
233  std::stringstream sstream;
234  // Frage hat die Form {rationale Zahl, rationale Zahl,Operator}
235  sstream << std::get<0>(q) // lese erste rationale Zahle
236  << ops[static_cast<size_t>(std::get<2>(q))] // operator
237  << std::get<1>(q); // lese zweite rationale Zahl
238  return sstream.str();
239  }
240 
246  inline Rational read(const std::string &answer) {
247  size_t l;
248  //extrahiere Zähler z.B. 2 von 2/3
249  long long num = std::stoll(answer, &l);
250  //l zeigt auf /
251  if (l == answer.size() || answer.at(l) != '/') {
252  throw std::runtime_error("expected /");
253  }
254  //Rest der Zeichenkette z.B. /3
255  auto part2 = answer.substr(l + 1);
256  //extrahiere Nenner
257  long long den = std::stoll(part2, &l, 10);
258  if (l != part2.size()) {
259  throw std::runtime_error("syntax error");
260  }
261  return { num, den };
262  }
263 }; //TestUI
264 
268 inline const std::array<std::string, 4> TestUI::ops{ "+", "-", "*", "/" };
269 
273 inline void ex1() {
274  //Erzeuge einen Container mit rationalen Zahlen
275  //lege den größten Nenner fest.
276  auto fn = [](long long lmaxden) {
277  std::vector<TestUI::Rational> data;
278  for (long long idx = 1; idx < lmaxden; idx++) {
279  for (long long jdx = idx + 1; jdx < lmaxden; jdx++) {
280  data.push_back({ idx, jdx });
281  }
282  }
283  return data;
284  };
285  //Instanz eines Tests
286  TestUI quiz{ 5, fn(10) };
287  //starte Test
288  quiz.run();
289 }
290 
291 } // namespace nmx::apps::x017
void run()
run Interaktion mit dem Benutzer
Definition: x017.h:187
TestGenerator(size_t maxq, const DataC &data)
TestGenerator Konstruktor.
Definition: x017.h:92
Rational read(const std::string &answer)
read Lese Antwort des Benutzers
Definition: x017.h:246
The TestUI class Test mit rationalen Zahlen Benutzerschnittstelle.
Definition: x017.h:159
bool has_questions()
has_questions
Definition: x017.h:122
auto next_question()
next_question generiere eine Frage
Definition: x017.h:111
std::vector< Rational > DataC
Definition: x017.h:21
std::tuple< Rational, Rational, Ops > Question
Definition: x017.h:25
Rational Klasse für rationale Zahlen.
Definition: x011.h:21
static std::string to_string(const Test::Question &q)
to_string Umwandlung Frage in Zeichenkette
Definition: x017.h:232
void ex1()
cppx3 Beispiel Test mit rationalen Zahlen
Definition: x017.h:273
TestUI(size_t maxq, const Test::DataC &data)
TestUI Konstruktor.
Definition: x017.h:180
static T calc_result(const Question &q)
get_result berechne korrekte Antwort
Definition: x017.h:129
double read(const char *label)
Definition: x000.h:91
const size_t _maxScore
Definition: x017.h:172
static const std::array< std::string, 4 > ops
Definition: x017.h:168
The TestGenerator class Erzeugt Fragen (Aufgaben) zum Rechnen mit Zahlen. T kann eine ganze eine rati...
Definition: x017.h:16