From d23b1556222ac75ac486f187b20e8022ddb5dc30 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Sat, 14 Mar 2009 12:03:19 -0700 Subject: [PATCH] Add support for timed-response categories 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 | 68 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 14 deletions(-) diff --git a/mnemon.c b/mnemon.c index fd7b9bf..4c5acd2 100644 --- a/mnemon.c +++ b/mnemon.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -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); -- 2.43.0