]> git.cworth.org Git - mnemon/commitdiff
Add support for timed-response categories
authorCarl Worth <cworth@cworth.org>
Sat, 14 Mar 2009 19:03:19 +0000 (12:03 -0700)
committerCarl Worth <cworth@cworth.org>
Sat, 14 Mar 2009 19:03:19 +0000 (12:03 -0700)
The syntax is, for example 'time = 2.0' in the data file, meaning
that each answer in that category needs to be answered in less than
2.0 seconds or it will be handled as an incorrect answer even if
correct. (Good for practicing multiplication facts, for example.)

mnemon.c

index fd7b9bf15b4ce7da6b6772420cfeea79e4028a39..4c5acd26287ce381e13a25efa86c81eb800536da 100644 (file)
--- a/mnemon.c
+++ b/mnemon.c
@@ -25,6 +25,7 @@
 #include <math.h>
 
 #include <sys/types.h>
+#include <sys/time.h>
 #include <dirent.h>
 #include <errno.h>
 #include <string.h>
@@ -61,6 +62,8 @@ typedef struct _category {
 
     /* Support sequential introduction of items from bin 0 */
     category_order_t order;
+    /* Support categories where responses are timed (0.0 == disable). */
+    double time_limit;
     int bin_zero_head;
 } category_t;
 
@@ -187,6 +190,7 @@ category_init (category_t *category,
     category->num_items = 0;
     category->items = NULL;
     category->order = CATEGORY_ORDER_RANDOM;
+    category->time_limit = 0.0;
     category->bin_zero_head = 0;
 }
 
@@ -254,6 +258,8 @@ category_print (category_t  *category,
 
     fprintf (file, "order = %s\n\n",
            category->order == CATEGORY_ORDER_RANDOM ? "random" : "sequential");
+    fprintf (file, "time = %f\n\n",
+            category->time_limit);
 
     for (i = 0; i < category->num_items; i++) {
        item = &category->items[i];
@@ -598,6 +604,19 @@ mnemon_load_category (mnemon_t             *mnemon,
                         value, path, line_count);
                exit (1);
            }
+       } else if (strcmp (name, "time") == 0) {
+           double limit;
+           char *end;
+           limit = strtod (value, &end);
+           while (isspace (*end))
+               end++;
+           if (*end == '\0') {
+               category->time_limit = limit;
+           } else {
+               fprintf (stderr, "Failed to parse time value: %s at %s:%d\n",
+                        value, path, line_count);
+               exit (1);
+           }
        } else {
            fprintf (stderr, "Unknown option %s at %s:%d\n",
                     name, path, line_count);
@@ -835,10 +854,13 @@ mnemon_item_in_category_of_length (void *closure, item_t *item)
 static void
 mnemon_select_item (mnemon_t    *mnemon,
                    bin_t       **bin_ret,
-                   int          *item_index_ret)
+                   int          *item_index_ret,
+                   category_t  **category_ret)
 {
     int bin_index, item_index;
     bin_t *bin;
+    item_t *item;
+    category_t *category;
 
     bin_index = rand_within_exponential (mnemon->num_bins);
 
@@ -846,14 +868,10 @@ mnemon_select_item (mnemon_t       *mnemon,
 
     item_index = rand_within (bin->num_items);
 
-    if (bin->score == 0) {
-       category_t *category;
-       item_t *item;
-
-       item = bin->items[item_index];
-
-       category = mnemon_item_category (mnemon, item);
+    item = bin->items[item_index];
+    category = mnemon_item_category (mnemon, item);
 
+    if (bin->score == 0) {
        if (category->order == CATEGORY_ORDER_SEQUENTIAL) {
            item = category_next_bin_zero_item (category);
            if (item)
@@ -863,6 +881,7 @@ mnemon_select_item (mnemon_t         *mnemon,
 
     *bin_ret = bin;
     *item_index_ret = item_index;
+    *category_ret = category;
 }
 
 
@@ -997,7 +1016,9 @@ mnemon_handle_response (mnemon_t   *mnemon,
                        bin_t           *bin,
                        int              item_index,
                        item_t          *item,
-                       const char      *response)
+                       const char      *response,
+                       double           response_time,
+                       double           time_limit)
 {
     bool_t correct;
 
@@ -1014,7 +1035,9 @@ mnemon_handle_response (mnemon_t  *mnemon,
        mnemon_remove_bin (mnemon, bin);
     }
 
-    if (correct) {
+    if (correct &&
+       (time_limit == 0.0 || response_time < time_limit))
+    {
        item->score++;
        mnemon->to_master--;
        /* We reserve an item score of 0 for an item that has
@@ -1033,8 +1056,12 @@ mnemon_handle_response (mnemon_t *mnemon,
            printf ("Masterful (%dx).", item->score);
        }
     } else {
-       printf ("  %s is the correct answer.",
-               item->response);
+       if (! correct)
+           printf ("  %s is the correct answer.",
+                   item->response);
+       else
+           printf ("Correct, but not quite quick enough (%0.2f seconds---needed %0.2f seconds)\n",
+                   response_time, time_limit);
        /* Penalize an incorrect response by forcing the score
         * negative. */
        if (item->score >= 0) {
@@ -1079,6 +1106,7 @@ mnemon_do_challenges (mnemon_t *mnemon)
     bin_t *bin;
     int item_index;
     item_t *item;
+    category_t *category;
     char *response;
     int i;
 
@@ -1113,16 +1141,25 @@ mnemon_do_challenges (mnemon_t *mnemon)
     printf ("\n");
 
     do {
-       mnemon_select_item (mnemon, &bin, &item_index);
+       struct timeval start, end;
+       
+       mnemon_select_item (mnemon, &bin, &item_index, &category);
        item = bin->items[item_index];
 
        if (bin->score == 0)
            mnemon->to_introduce--;
 
        while (1) {
+           if (category->time_limit > 0.0) {
+               readline ("The next one is timed. Press enter when ready:");
+           }
+               
            printf ("%s\n", item->challenge);
 
+           gettimeofday (&start, NULL);
            response = readline ("> ");
+           gettimeofday (&end, NULL);
+
            /* Terminate on EOF */
            if (response == NULL) {
                printf ("\n");
@@ -1136,7 +1173,10 @@ mnemon_do_challenges (mnemon_t *mnemon)
        }
 
        mnemon_handle_response (mnemon, bin, item_index,
-                               item, response);
+                               item, response,
+                               (end.tv_sec + end.tv_usec / 1e6) -
+                               (start.tv_sec + start.tv_usec / 1e6),
+                               category->time_limit);
     } while (mnemon->to_introduce ||
             mnemon->unlearned ||
             mnemon->to_master > 0);