/*****************************************************************************/ /* Klavaro - a flexible touch typing tutor */ /* Copyright (C) 2005, 2006, 2007, 2008 Felipe Castro */ /* Copyright (C) 2009, 2010, 2011 Free Software Foundation */ /* */ /* This program is free software, licensed under the terms of the GNU */ /* General Public License as published by the Free Software Foundation, */ /* either version 3 of the License, or (at your option) any later version. */ /* You should have received a copy of the GNU General Public License */ /* along with this program. If not, see . */ /*****************************************************************************/ #include #include #include #include #include #include #include "main.h" #include "auxiliar.h" #include "keyboard.h" #include "adaptability.h" #include "accuracy.h" static struct { gunichar uchr; gulong wrong; gulong correct; } terror[MAX_CHARS_EVALUATED]; static gint terror_n; /* Simple reset */ void accur_reset () { terror_n = 0; } gint accur_n_get (void) { return (terror_n); } gchar * accur_char_utf8 (gint i) { gchar *utf8; gint n; if (i < 0 || i >= terror_n) return (g_strdup (" ")); utf8 = g_malloc (UTF8_BUFFER); n = g_unichar_to_utf8 (terror[i].uchr, utf8); if (n < 1) return (g_strdup (" ")); utf8[n] = '\0'; return (utf8); } gulong accur_wrong_get (gint i) { if (i < 0 || i >= terror_n) return -1; return (terror[i].wrong); } /********************************************************************** * Open the accuracy accumulator */ void accur_init () { const gchar delim[] = "\t\n\r"; gint i; gchar *tmp; gchar *data; gboolean success; gunichar uchr; gulong wrong; gulong correct; memset (&terror, 0, sizeof (terror)); terror_n = 0; tmp = g_build_filename (main_path_stats (), ACCUR_LOG_FILE, NULL); success = g_file_get_contents (tmp, &data, NULL, NULL); g_free (tmp); if (!success) return; tmp = strtok (data, delim); for (i = 0; i < MAX_CHARS_EVALUATED; i++) { if (tmp == NULL) break; uchr = g_utf8_get_char_validated (tmp, -1); if (uchr == (gunichar)-1 || uchr == (gunichar)-2) break; tmp = strtok (NULL, delim); if (tmp == NULL) break; wrong = strtoul (tmp, NULL, 10); tmp = strtok (NULL, delim); if (tmp == NULL) break; correct = strtoul (tmp, NULL, 10); if (wrong > 0 && correct <= ERROR_INERTIA) { terror[i].uchr = uchr; terror[i].wrong = wrong; terror[i].correct = correct; terror_n = i + 1; } else break; tmp = strtok (NULL, delim); } g_free (data); } /********************************************************************** * Accumulates correctly typed characters */ void accur_correct (gunichar uchr) { gint i; if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b') return; for (i = 0; i < terror_n; i++) { if (uchr == terror[i].uchr) { if (terror[i].correct > ERROR_INERTIA) { terror[i].wrong -= (terror[i].wrong == 0 ? 0 : 1); terror[i].correct = 1; } else terror[i].correct++; return; } } } /********************************************************************** * Accumulates mistyped characters */ void accur_wrong (gunichar uchr) { gint i; gint i_min = -1; gdouble correct_min = 1e9; gdouble hlp; if (uchr == L' ' || uchr == UPSYM || uchr == L'\t' || uchr == L'\b') return; for (i = 0; i < terror_n; i++) { if (uchr == terror[i].uchr) { terror[i].wrong++; return; } hlp = ((gdouble) terror[i].wrong) / ((gdouble) terror[i].correct + terror[i].wrong + 1); if (hlp <= correct_min) { correct_min = hlp; i_min = i; } } if (terror_n < MAX_CHARS_EVALUATED) { i = terror_n; terror_n++; } else { i = (i_min > -1 ? i_min : terror_n - 1); } terror[i].uchr = uchr; terror[i].wrong = 1; terror[i].correct = 1; } gulong accur_error_total () { gint i; gulong n = 0; for (i = 0; i < terror_n; i++) n += (terror[i].wrong < 12345 ? terror[i].wrong : 0); return n; } /******************************************************************************* * Increasing order, first: wrongness, second: correctness */ void accur_sort () { gint i, j; gunichar uchr; gulong correct; gulong wrong; for (i = 1; i < terror_n; i++) { for (j = i; j > 0; j--) { if (terror[j].correct < terror[j-1].correct) { uchr = terror[j].uchr; terror[j].uchr = terror[j-1].uchr; terror[j-1].uchr = uchr; wrong = terror[j].wrong; terror[j].wrong = terror[j-1].wrong; terror[j-1].wrong = wrong; correct = terror[j].correct; terror[j].correct = terror[j-1].correct; terror[j-1].correct = correct; } } } for (i = 1; i < terror_n; i++) { for (j = i; j > 0; j--) { if (terror[j].wrong > terror[j-1].wrong) { uchr = terror[j].uchr; terror[j].uchr = terror[j-1].uchr; terror[j-1].uchr = uchr; wrong = terror[j].wrong; terror[j].wrong = terror[j-1].wrong; terror[j-1].wrong = wrong; correct = terror[j].correct; terror[j].correct = terror[j-1].correct; terror[j-1].correct = correct; } } } } /******************************************************************************* * Creates a random weird word based on error profile */ gboolean accur_create_word (gunichar word[MAX_WORD_LEN + 1]) { gint i, j; gint ind; gint n; gunichar vowels[20]; gunichar last = 0; gint vlen; if (terror_n < 10) return FALSE; vlen = keyb_get_vowels (vowels); n = rand () % (MAX_WORD_LEN) + 1; for (i = 0; i < n; i++) { for (j = 0; j < 100; j++) { ind = rand () % terror_n; if (terror[ind].uchr == last) continue; if (rand () % terror[0].wrong < terror[ind].wrong) break; } word[i] = terror[ind].uchr; last = word[i]; /* Avoid double diacritics */ if (i > 0) if (keyb_is_diacritic (word[i - 1]) && keyb_is_diacritic (word[i])) { word[i] = vowels[rand () % vlen]; last = word[i]; } } /* * Null terminated unistring */ word[n] = L'\0'; return TRUE; } /******************************************************************************* * Saves the accuracy accumulator */ void accur_close () { gint i; gchar *tmp; gchar *utf8; FILE *fh; tmp = g_build_filename (main_path_stats (), ACCUR_LOG_FILE, NULL); fh = g_fopen (tmp, "wb"); g_free (tmp); if (!fh) { g_message ("Could not save the accuracy log file at %s", main_path_stats ()); return; } accur_sort (); for (i = 0; i < terror_n; i++) { if (terror[i].wrong == 0 || terror[i].correct > ERROR_INERTIA) continue; utf8 = accur_char_utf8 (i); g_fprintf (fh, "%s\t%lu\t%lu\n", utf8, terror[i].wrong, terror[i].correct); g_free (utf8); } fclose (fh); }