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