1 /***************************************************************************
2 Aldo
3 --------------------
4 Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Giuseppe "denever" Martino
5 begin : Sun May 6 2001
6 email : denever@users.sourceforge.net
7 ***************************************************************************/
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the Free Software *
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
23 * MA 02110-1301 USA *
24 * *
25 ***************************************************************************/
26
27 #include "resources.hh"
28 #include "blocks.hh"
29 #include "koch.hh"
30
31 #include <iostream>
32 #include <string>
33 #include <sstream>
34 #include <list>
35 #include <map>
36
37 // width of screen in characters
38 #define TERMINAL_WIDTH 80
39
40 // markers for the correspondence between keyed symbol and copied symbol
41 #define CORRECT_MARKER '.'
42 #define INCORRECT_MARKER '!'
43 #define MISSED_MARKER '@'
44
45 using namespace std;
46
47 typedef map<char, unsigned int>::const_iterator c_map;
48 typedef list< string >::const_iterator c_lststr;
49 typedef string::const_iterator c_str;
50
51 /*
52 * Returns a string truncated or padded to a given length. The padding is to
53 * the right and the padding character is 'MISSED_MARKER'.
54 */
padding(unsigned int len,const string & b)55 string padding(unsigned int len, const string& b)
56 {
57 int s = len - b.size();
58 if(s > 0)
59 return b + string(s,MISSED_MARKER);
60
61 if(s < 0)
62 return string(b, 0, len);
63
64 return b;
65 }
66
67 /*
68 * This function computes the width in characters for representing an integer
69 * in base 10. It is useful in determining the maximum with needed for
70 * displaying a loop counter, such that vertical alignment can be achieved.
71 */
width_base_10(const size_t n)72 size_t width_base_10(const size_t n)
73 {
74 size_t n_width = 0;
75 for (size_t n_max = n; n_max > 0; n_max /= 10) {
76 ++n_width;
77 }
78 return n_width;
79 }
80
81 /*
82 * This function displays the copy success rate.
83 */
display_overall_rate(const unsigned int percentage)84 void display_overall_rate(const unsigned int percentage)
85 {
86 cout<<endl<<ovrmsg_title<<": ";
87 cout.width(3);
88 cout<<percentage<<'%'<<endl;
89 }
90
91 /*
92 * This function displays the copy success rate per keyed symbol.
93 */
display_symbol_rate(const list<string> & lks,const list<string> & lcs)94 void display_symbol_rate(const list<string>& lks, const list<string>& lcs)
95 {
96 map<char, unsigned int> keyed_all;
97 map<char, unsigned int> keyed_bad;
98 map<char, unsigned int> keyed_missed;
99 map<char, unsigned int> copied_all;
100 map<char, unsigned int> copied_good;
101 map<char, unsigned int> copied_bad;
102
103 // compute results
104
105 // iterate over the lists of strings
106 c_lststr lks_it;
107 c_lststr lcs_it;
108 for(lks_it = lks.begin(), lcs_it = lcs.begin();
109 lks_it != lks.end() && lcs_it != lcs.end();
110 ++lks_it, ++lcs_it
111 )
112 {
113 // ensure that the copied string is as long as the keyed string,
114 // padding it with 'MISSED_MARKER' if necessary
115 string copied = padding((*lks_it).size(), *lcs_it);
116
117 // iterate over the characters in each group
118 c_str kit;
119 c_str cit;
120 for(kit = (*lks_it).begin(), cit = copied.begin();
121 kit != (*lks_it).end();
122 ++kit, ++cit
123 )
124 {
125 char kc = *kit; // keyed symbol
126 char cc = *cit; // copied sybmol
127
128 keyed_all[kc]++; // mark keyed symbol
129 copied_all[cc]++; // mark copied symbol
130
131 if(kc == cc)
132 {
133 // keyed symbol was copied correctly
134 copied_good[cc]++;
135 }
136 else if(cc != MISSED_MARKER)
137 {
138 // keyed symbol was copied incorrectly
139 keyed_bad[kc]++;
140 copied_bad[cc]++;
141 }
142 else
143 {
144 // keyed symbol was missed
145 keyed_missed[kc]++;
146 }
147 }
148 }
149
150 // display results
151
152 cout<<endl<<smbmsg_title<<':'<<endl; // display feedback category "title"
153 cout << '"' << smbmsg_symbol << ':'
154 << smbmsg_keyed << " = " << smbmsg_correct << " + " << smbmsg_incorrect << " + " << smbmsg_missed
155 << " (" << smbmsg_percent << "); "
156 << smbmsg_copied << " = " << smbmsg_correct << " + " << smbmsg_incorrect
157 << " (" << smbmsg_percent << ")\"" << endl
158 << endl;
159
160 // loop over maps
161 char c;
162 size_t count_width = width_base_10(lks.size() * lks.front().size());
163 for(c_map mtc = keyed_all.begin(); mtc != keyed_all.end(); ++mtc)
164 {
165 c = (*mtc).first;
166
167 // symbol
168 cout << c << ":";
169
170 // sum of counts for keyed symbols
171 cout.width(count_width);
172 cout << keyed_all[c] << " = ";
173 cout.width(count_width);
174 cout << copied_good[c] << " + ";
175 cout.width(count_width);
176 cout << keyed_bad[c] << " + ";
177 cout.width(count_width);
178 cout << keyed_missed[c];
179
180 // sum of percentages for keyed symbols
181 cout << " (";
182 if (keyed_all[c] != 0)
183 {
184 cout.width(3);
185 cout << 100*copied_good[c]/keyed_all[c] << "% + ";
186 cout.width(3);
187 cout << 100*keyed_bad[c]/keyed_all[c] << "% + ";
188 cout.width(3);
189 cout << 100*keyed_missed[c]/keyed_all[c] << '%';
190 }
191 else
192 {
193 cout << "?% + ?% + ?%";
194 }
195 cout << "); ";
196
197 // sum of counts for copied symbols
198 cout.width(count_width);
199 cout << copied_all[c] << " = ";
200 cout.width(count_width);
201 cout << copied_good[c] << " + ";
202 cout.width(count_width);
203 cout << copied_bad[c];
204
205 // sum of percentages for copied symbols
206 cout << " (";
207 if (copied_all[c] != 0)
208 {
209 cout.width(3);
210 cout << 100*copied_good[c]/copied_all[c] << "% + ";
211 cout.width(3);
212 cout << 100*copied_bad[c]/copied_all[c] << '%';
213 }
214 else
215 {
216 cout << "?% + ?%";
217 }
218 cout << ')' << endl;
219 }
220 }
221
222 /*
223 * This function takes a keyed sign group and a copied sign group and
224 * generates a mistakes string of the same length. The mistakes string
225 * contains mistake marker characters at the positions where the keyed
226 * and the copied strings differ and unobtrusive spacer characters at
227 * the other positions.
228 */
mark_mistakes(const string keyed,const string copied)229 string mark_mistakes(const string keyed, const string copied)
230 {
231 string mistakes; // start with empty string
232
233 c_str kit = keyed.begin();
234 c_str cit = copied.begin();
235
236 while(kit != keyed.end())
237 if(cit != copied.end())
238 {
239 if(*kit == *cit)
240 mistakes += CORRECT_MARKER; // unobtrusive spacer
241 else if(*cit == MISSED_MARKER)
242 mistakes += MISSED_MARKER; // missed symbol
243 else
244 mistakes += INCORRECT_MARKER; // incorrect copy
245 ++kit;
246 ++cit;
247 }
248 else
249 {
250 mistakes += MISSED_MARKER;
251 ++kit;
252 }
253
254 return mistakes;
255 }
256
get_marked_strings(const list<string> & lks,const list<string> & lcs)257 list<string> get_marked_strings(const list<string>& lks, const list<string>& lcs)
258 {
259 list< string > lms; //list of marked strings
260
261 c_lststr lks_it = lks.begin();
262 c_lststr lcs_it = lcs.begin();
263
264 while(lks_it != lks.end())
265 if(lcs_it != lcs.end())
266 {
267 lms.push_back(mark_mistakes(*lks_it, *lcs_it));
268 ++lks_it;
269 ++lcs_it;
270 }
271 else
272 {
273 lms.push_back( string((*lks_it).size(), INCORRECT_MARKER) );
274 ++lks_it;
275 }
276
277 return lms;
278 }
279
280 /*
281 * This function displays the keyed sign groups and the copied sign groups
282 * alongside each other, accompanied by markers which indicate the positions
283 * of the copying mistakes.
284 */
display_comparison(const list<string> & lks,const list<string> & lcs)285 void display_comparison(const list<string>& lks, const list<string>& lcs)
286 {
287 // display evaluation header
288
289 cout<<endl<<cmpmsg_title<<':'<<endl; // display feedback category "title"
290
291 string legend_indent = string( string(cmpmsg_group).length(), ' '); // indentation string
292 cout << '"' << cmpmsg_group << ':'
293 << '\'' << CORRECT_MARKER << "'=" << cmpmsg_correct << ", "
294 << '\'' << MISSED_MARKER << "'=" << cmpmsg_missed << ", "
295 << '\'' << INCORRECT_MARKER << "'=" << cmpmsg_incorrect << endl
296 << legend_indent << "k:" << cmpmsg_keyed << endl
297 << legend_indent << "c:" << cmpmsg_copied << '"' << endl
298 << endl;
299
300 // build evaluation cells (on three rows)
301
302 // combine parameter lists into a list of mistake strings
303 list<string> lms = get_marked_strings(lks, lcs);
304
305 // temporary variables for preparing output lines
306 ostringstream mistaken_row_formatter;
307 string mistaken_row;
308 string keyed_row;
309 string copied_row;
310
311 // how many characters are needed for representing the highest index
312 size_t n_width = width_base_10(lks.size());
313
314 // indentation string for keyed and copied strings
315 string indent = string(n_width, ' ');
316
317 // format the evaluation strings
318 // (loop with iterators over the lists of strings)
319 unsigned int n;
320 c_lststr lms_it = lms.begin();
321 c_lststr lks_it = lks.begin();
322 c_lststr lcs_it = lcs.begin();
323 for(
324 n = 1
325 ;
326 lms_it != lms.end() &&
327 lks_it != lks.end() &&
328 lcs_it != lcs.end()
329 ;
330 ++n,
331 ++lms_it,
332 ++lks_it,
333 ++lcs_it
334 )
335 {
336 mistaken_row_formatter << " ";
337 mistaken_row_formatter.width(n_width);
338 mistaken_row_formatter << n << ":" << *lms_it;
339
340 keyed_row += indent + "k:" + *lks_it;
341
342 copied_row += indent + "c:" + padding((*lks_it).size(), *lcs_it);
343 }
344 mistaken_row = mistaken_row_formatter.str();
345
346 // display evaluation cells, breaking the lines at or before TERMINAL_WIDTH
347
348 // (TERMINAL_WIDTH / cell_width) feedback cells fit on a row
349 // cell_width is the sum of:
350 // 1 character blank to separate the cells
351 // n_width characters for the counter
352 // 1 character blank between the counter and the sign group
353 // size() characters for the sign group
354 unsigned int cell_width = 2 + n_width + lks.front().size();
355
356 // how many characters to use in a screen row,
357 // such that lines break at cell border
358 unsigned int used_width = (TERMINAL_WIDTH / cell_width) * cell_width;
359
360 // loop over the screen rows
361 for(size_t pos = 0; pos < mistaken_row.size(); pos += used_width)
362 {
363 // display output lines
364 cout << mistaken_row.substr(pos, used_width) << endl; // e.g. 12:!.!.@@!
365 cout << keyed_row.substr(pos, used_width) << endl; // eishtmo
366 cout << copied_row.substr(pos, used_width) << endl; // aibh@@d
367 cout << endl;
368 }
369 }
370
371 /*
372 * This function lets the user input the copied sign groups
373 */
get_copied_strings(unsigned int num_groups)374 list<string> get_copied_strings(unsigned int num_groups)
375 {
376 list<string> lcs;
377
378 cout << endl << chkmsg_1 << endl;
379 cout << chkmsg_2 << endl;
380
381 // how many characters are needed for representing the highest index
382 int i_width = width_base_10(num_groups);
383
384 for(unsigned int i=0; i<num_groups; i++)
385 {
386 string tmp;
387
388 do
389 {
390 cout.width(i_width);
391 cout << (i+1) << ": ";
392 cin.clear();
393 }
394 while(! getline(cin, tmp));
395
396 lcs.push_back(tmp);
397 }
398 return lcs;
399 }
400
401 /*
402 * This function compares two strings and returns lets the user input the copied sign groups
403 */
count_wrong_letters(string keyed,string copied)404 unsigned int count_wrong_letters(string keyed, string copied)
405 {
406 unsigned int wrong_letters = 0;
407
408 c_str kit = keyed.begin();
409 c_str cit = copied.begin();
410
411 while(kit != keyed.end())
412 if(cit != copied.end())
413 {
414 if(*kit != *cit)
415 wrong_letters++;
416
417 ++kit;
418 ++cit;
419 }
420 else
421 {
422 wrong_letters++;
423 ++kit;
424 }
425
426 //wrong_letters = keyed.size(); // if copied key has a different lenght of keyed all letters are wrong
427
428 return wrong_letters;
429 }
430
get_overall_rate(const list<string> & lks,const list<string> & lcs)431 unsigned int get_overall_rate(const list<string>& lks, const list<string>& lcs)
432 {
433 double wrong_letters = 0.0;
434 double total = 0.0;
435
436 c_lststr lks_it = lks.begin();
437 c_lststr lcs_it = lcs.begin();
438
439 while(lks_it != lks.end())
440 {
441 total += (*lks_it).size();
442
443 if(lcs_it != lcs.end())
444 {
445
446 wrong_letters += count_wrong_letters(*lks_it, *lcs_it);
447
448 ++lks_it;
449 ++lcs_it;
450 }
451 else
452 {
453 wrong_letters += (*lks_it).size();
454 ++lks_it;
455 }
456 }
457
458 double wrong_tax = wrong_letters/total;
459
460 return int(100 - 100 * wrong_tax);
461 }
462
463
464 /*
465 * This function lets the user input the copied sign groups and displays
466 * information about how well an exercise was completed.
467 * Its goal is to help the user discover the areas where improvement is possible.
468 */
check(const libexercises::Blocks & current_exercise)469 unsigned int check(const libexercises::Blocks& current_exercise)
470 {
471 list<string> lks = current_exercise.tokenize(); // list of keyed strings
472 list<string> lcs = get_copied_strings(lks.size()); // list of copied strings
473
474 cout << endl << fbkmsg_title << endl; // introduce the feedback information
475
476 unsigned int overall_rate = get_overall_rate(lks, lcs); // overall success rate
477
478 display_overall_rate(overall_rate); // display overall success rate
479 display_symbol_rate(lks, lcs); // display success rate per keyed symbol
480 display_comparison(lks, lcs); // display comparison between keyed and copied sign groups
481
482 cout<<endl;
483
484 return overall_rate;
485 }
486
487 /*
488 * This function lets the user input the copied sign groups and displays
489 * feedback about how it went.
490 */
check(const libexercises::Koch & current_exercise)491 unsigned int check(const libexercises::Koch& current_exercise)
492 {
493 list<string> lks = current_exercise.tokenize(); // list of keyed strings
494 list<string> lcs = get_copied_strings(lks.size()); // list of copied strings
495
496 cout << endl << fbkmsg_title << endl; // introduce the feedback information
497
498 unsigned int overall_rate = get_overall_rate(lks, lcs); // overall success rate
499
500 display_overall_rate(overall_rate); // display overall success rate
501 display_symbol_rate(lks, lcs); // display success rate per keyed symbol
502 display_comparison(lks, lcs); // display comparison between keyed and copied sign groups
503
504 cout<<endl;
505
506 return overall_rate;
507 }
508