]> git.cworth.org Git - mnemon/blobdiff - mnemon.c
Break mnemon up into a main program and a mnemon "library"
[mnemon] / mnemon.c
index 054e21cb59494210f1eccfb6b236482beb6ca92f..f869993061de67ad9332bbe3b77a8753ab882310 100644 (file)
--- a/mnemon.c
+++ b/mnemon.c
@@ -1,9 +1,10 @@
-/*
- * Copyright © 2006 Carl Worth
+/* mnemon - A memory training library
+ *
+ * Copyright © 2006,2011 Carl Worth
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
+ * the Free Software Foundation; either version 3, or (at your option)
  * any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -16,6 +17,8 @@
  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."
  */
 
+#include "mnemon.h"
+
 /* for asprintf */
 #define _GNU_SOURCE
 #include <stdio.h>
@@ -26,6 +29,7 @@
 
 #include <sys/types.h>
 #include <sys/time.h>
+#include <sys/stat.h>
 #include <unistd.h>
 #include <dirent.h>
 #include <errno.h>
@@ -41,6 +45,8 @@ do {                                  \
     assert (NOT_REACHED);              \
 } while (0)
 
+#define unused(foo) foo __attribute__((unused))
+
 typedef int bool_t;
 
 typedef struct _item {
@@ -49,12 +55,12 @@ typedef struct _item {
     char *response;
 } item_t;
 
-typedef struct _bin {
+struct _bin {
     int score;
     int items_size;
     int num_items;
     item_t **items;
-} bin_t;
+};
 
 typedef enum {
     CATEGORY_ORDER_RANDOM,
@@ -64,10 +70,12 @@ typedef enum {
 typedef enum {
     CHALLENGE_TYPE_TEXT,
     CHALLENGE_TYPE_IMAGE,
-    CHALLENGE_TYPE_AUDIO
+    CHALLENGE_TYPE_AUDIO,
+    CHALLENGE_TYPE_MIDI,
+    CHALLENGE_TYPE_TEXT_TO_SPEECH
 } challenge_type_t;
 
-typedef struct _category {
+struct _category {
     char *name;
     int items_size;
     int num_items;
@@ -80,24 +88,9 @@ typedef struct _category {
     int bin_zero_head;
     /* Support challenges of non-text types (image, audio, etc.) */
     challenge_type_t challenge_type;
-} category_t;
-
-typedef struct _mnemon {
-    char *dir_name;
-
-    int categories_size;
-    int num_categories;
-    category_t *categories;
-
-    int bins_size;
-    int num_bins;
-    bin_t *bins;
-
-    int to_introduce;
-    int to_master;
-    int unlearned;
-    int mastered;
-} mnemon_t;
+    /* Whether to repeat afterwards (for a little extra reinforcement) */
+    bool_t repeat;
+};
 
 static void *
 xmalloc (size_t size)
@@ -208,6 +201,7 @@ category_init (category_t *category,
     category->time_limit = 0.0;
     category->bin_zero_head = 0;
     category->challenge_type = CHALLENGE_TYPE_TEXT;
+    category->repeat = 0;
 }
 
 static void
@@ -288,9 +282,17 @@ category_print (category_t *category,
     case CHALLENGE_TYPE_AUDIO:
        fprintf (file, "audio");
        break;
+    case CHALLENGE_TYPE_MIDI:
+       fprintf (file, "midi");
+       break;
+    case CHALLENGE_TYPE_TEXT_TO_SPEECH:
+       fprintf (file, "text-to-speech");
+       break;
     }
     fprintf (file, "\n\n");
 
+    fprintf (file, "repeat = %d\n\n", category->repeat);
+
     for (i = 0; i < category->num_items; i++) {
        item = &category->items[i];
        if (i != 0)
@@ -394,7 +396,7 @@ bin_num_items_matching (bin_t                       *bin,
     return num_items;
 }
 
-static void
+void
 mnemon_init (mnemon_t *mnemon)
 {
     char *home;
@@ -419,7 +421,7 @@ mnemon_init (mnemon_t *mnemon)
     mnemon->mastered = -1;
 }
 
-static void
+void
 mnemon_fini (mnemon_t *mnemon)
 {
     int i;
@@ -563,7 +565,7 @@ trim_space (char *string)
     return string;
 }
 
-static void
+void
 mnemon_load_category (mnemon_t         *mnemon,
                      const char        *name)
 {
@@ -575,6 +577,7 @@ mnemon_load_category (mnemon_t              *mnemon,
     char *path;
     category_t *category;
     int i;
+    struct stat st;
 
     path = xmalloc (strlen (mnemon->dir_name) + 1 + strlen (name) + 1);
     sprintf (path, "%s/%s", mnemon->dir_name, name);
@@ -586,6 +589,12 @@ mnemon_load_category (mnemon_t             *mnemon,
        exit (1);
     }
 
+    fstat (fileno(file), &st);
+    if (! S_ISREG(st.st_mode)) {
+       fprintf (stderr, "Error: File %s is not a regular file.\n", path);
+       exit (1);
+    }
+
     category = mnemon_get_category (mnemon, name);
 
 #define READ_LINE do {                                 \
@@ -654,11 +663,20 @@ mnemon_load_category (mnemon_t            *mnemon,
                category->challenge_type = CHALLENGE_TYPE_IMAGE;
            } else if (strcmp (value, "audio") == 0) {
                category->challenge_type = CHALLENGE_TYPE_AUDIO;
+           } else if (strcmp (value, "midi") == 0) {
+               category->challenge_type = CHALLENGE_TYPE_MIDI;
+           } else if (strcmp (value, "text-to-speech") == 0) {
+               category->challenge_type = CHALLENGE_TYPE_TEXT_TO_SPEECH;
            } else {
                fprintf (stderr, "Unknown value for \"challenge\" option \"%s\" at %s:%d\n",
                         value, path, line_count);
                exit (1);
            }
+       } else if (strcmp (name, "repeat") == 0) {
+           if (strcmp (value, "0") == 0) 
+               category->repeat = 0;
+           else
+               category->repeat = 1;
        } else {
            fprintf (stderr, "Unknown option %s at %s:%d\n",
                     name, path, line_count);
@@ -718,7 +736,7 @@ mnemon_load_category (mnemon_t              *mnemon,
     }
 }
 
-static void
+void
 mnemon_load (mnemon_t *mnemon)
 {
     DIR *dir;
@@ -747,7 +765,7 @@ mnemon_load (mnemon_t *mnemon)
     closedir (dir);
 }
 
-static void
+void
 mnemon_save (mnemon_t *mnemon)
 {
     int i, err;
@@ -886,7 +904,7 @@ mnemon_item_in_category_of_length (void *closure, item_t *item)
     item_in_category_of_length_closure_t *iicolc = closure;
     mnemon_t *mnemon = iicolc->mnemon;
     category_t *category = iicolc->category;
-    int length = iicolc->length;
+    unsigned int length = iicolc->length;
 
     if (mnemon_item_category (mnemon, item) != category)
        return 0;
@@ -979,7 +997,7 @@ print_histogram_bar (double size,
     printf ("\n");
 }
 
-static void
+void
 mnemon_print_histogram (mnemon_t    *mnemon,
                        const char  *category_name,
                        int          length)
@@ -1041,6 +1059,7 @@ mnemon_handle_command (mnemon_t           *mnemon,
     const char *arg;
     int len;
     switch (command[0]) {
+       /* 'h' for histogram */
        case 'h':
        {
            char *category = NULL;
@@ -1059,6 +1078,12 @@ mnemon_handle_command (mnemon_t          *mnemon,
            mnemon_print_histogram (mnemon, category, length);
        }
        break;
+       /* 'r' for repeat */
+        case 'r':
+       {
+           /* Nothing necessary for repeating. */
+       }
+       break;
         default:
            printf ("Unknown command: %s\n", command);
            break;
@@ -1093,13 +1118,11 @@ mnemon_handle_response (mnemon_t        *mnemon,
        (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
         * never been asked. */
        if (item->score == 0) {
            item->score = 1;
            mnemon->unlearned--;
-           mnemon->to_master--;
            printf ("You got it!");
        } else if (item->score < 0) {
            printf ("Yes---just give me %d more.",
@@ -1108,6 +1131,8 @@ mnemon_handle_response (mnemon_t  *mnemon,
            printf ("On your first try, no less!");
        } else {
            printf ("Masterful (%dx).", item->score);
+           if (mnemon->to_master)
+               mnemon->to_master--;
        }
     } else {
        if (! correct)
@@ -1123,12 +1148,13 @@ mnemon_handle_response (mnemon_t        *mnemon,
                printf (" Oops, you knew that, right? (%dx)\n ",
                        item->score);
            mnemon->unlearned++;
-           /* We add three here, (rather than just 2 to track the
-            * change in the item's score below), as an extra
-            * penalty. If the user is forgetting stuff learned
-            * previously, then more time should be spent on mastering
-            * than learning new items. */
-           mnemon->to_master += item->score + 3;
+           /* We increase to_master here as an extra penalty. If the
+            * user is forgetting stuff learned previously, then more
+            * time should be spent on mastering than learning new
+            * items. Note that we only do this during the initial
+            * phase while new items are still being introduced. */
+           if (mnemon->to_introduce)
+               mnemon->to_master++;
            /* We go to -2 to force a little extra reinforcement
             * when re-learning an item, (otherwise, it will often
             * get asked again immediately where it is easy to get
@@ -1136,7 +1162,6 @@ mnemon_handle_response (mnemon_t  *mnemon,
            item->score = -2;
        } else {
            item->score--;
-           mnemon->to_master++;
        }
     }
 
@@ -1182,6 +1207,12 @@ mnemon_show_challenge (mnemon_t *mnemon,
     case CHALLENGE_TYPE_AUDIO:
        program = "play";
        break;
+    case CHALLENGE_TYPE_MIDI:
+       program = "timidity -Os";
+       break;
+    case CHALLENGE_TYPE_TEXT_TO_SPEECH:
+       program = "mnemon-tts";
+       break;
     }
 
     xasprintf (&command, "%s %s/%s >/dev/null 2>&1 &",
@@ -1193,7 +1224,8 @@ mnemon_show_challenge (mnemon_t *mnemon,
 }
 
 static void
-mnemon_hide_challenge (mnemon_t *mnemon, challenge_type_t challenge_type)
+mnemon_hide_challenge (unused (mnemon_t *mnemon),
+                      challenge_type_t challenge_type)
 {
     char * command;
 
@@ -1208,7 +1240,7 @@ mnemon_hide_challenge (mnemon_t *mnemon, challenge_type_t challenge_type)
     free (command);
 }
 
-static void
+void
 mnemon_do_challenges (mnemon_t *mnemon)
 {
     bin_t *bin;
@@ -1291,43 +1323,14 @@ mnemon_do_challenges (mnemon_t *mnemon)
        free (response);
 
        /* Replay audio challenges for reinforcement. */
-       if (category->challenge_type == CHALLENGE_TYPE_AUDIO) {
+       if (category->repeat)
+       {
            mnemon_show_challenge (mnemon, category->challenge_type,
                                   item->challenge);
-           sleep (1);
+           printf ("%s\n", item->challenge);
+           sleep (2);
        }
     } while (mnemon->to_introduce ||
             mnemon->unlearned ||
             mnemon->to_master > 0);
 }
-
-int
-main (int argc, char *argv[])
-{
-    mnemon_t mnemon;
-    char *response;
-
-    srand (time (NULL));
-
-    mnemon_init (&mnemon);
-
-    mnemon_load (&mnemon);
-
-    mnemon_do_challenges (&mnemon);
-
-    mnemon_save (&mnemon);
-
-    mnemon_fini (&mnemon);
-
-    mnemon_init (&mnemon);
-    mnemon_load (&mnemon);
-
-    printf ("Great job.\nHere are your current results:\n");
-    mnemon_print_histogram (&mnemon, NULL, 0);
-    response = readline ("Press enter to quit.\n");
-    free (response);
-
-    mnemon_fini (&mnemon);
-
-    return 0;
-}