[Tux4kids-commits] r396 - tuxmath/trunk/src

tholy-guest at alioth.debian.org tholy-guest at alioth.debian.org
Sun Dec 30 13:00:47 UTC 2007


Author: tholy-guest
Date: 2007-12-30 13:00:46 +0000 (Sun, 30 Dec 2007)
New Revision: 396

Modified:
   tuxmath/trunk/src/fileops.c
   tuxmath/trunk/src/game.c
   tuxmath/trunk/src/mathcards.c
   tuxmath/trunk/src/mathcards.h
Log:
New feature & fix bugs.
Bug fixes:
  fileops.c: homedir setting in read_config_file lacked a closedir,
    resulting in a memory leak.
  fileops.c: write_postgame_summary did not close the file after
    appending, resulting in the summary sometimes never being
    "flushed" (some summary files have just been truncated).
  mathcards.c: new_randomize_list did not set the "previous" field of
    each node, resulting in memory corruption & drawing glitches
  mathcards.c: free tmp_vect (fixes memory leak)

New feature:
  mathcards & game & fileops: collect data on the amount of time each
    problem is on the screen, and report the median in the game
    summary files.



Modified: tuxmath/trunk/src/fileops.c
===================================================================
--- tuxmath/trunk/src/fileops.c	2007-12-28 17:07:28 UTC (rev 395)
+++ tuxmath/trunk/src/fileops.c	2007-12-30 13:00:46 UTC (rev 396)
@@ -210,6 +210,7 @@
 static int read_goldstars(void);
 static int read_lines_from_file(FILE *fp,char ***lines);
 static void dirname_up(char *dirname);
+static char* get_user_name(void);
 
 
 /* fix HOME on windows */
@@ -415,6 +416,11 @@
 
 
 /* This functions keep and returns the user data directory application path */
+/* FIXME?: currently the best way to test whether we're using the user's    */
+/* home directory, or using a different path, is to test add_subdir (which  */
+/* is 1 if we're using the user's ~/.tuxmath directory, 0 otherwise). Is    */
+/* this a bad example of using 1 thing for 2 purposes? So far there are     */
+/* no conflicts. */
 static char* user_data_dir = NULL;
 static int add_subdir = 1;
 static char* high_scores_file_path = NULL;
@@ -1258,8 +1264,8 @@
 {
   char buf[PATH_MAX];
   char *parameter, *param_begin, *param_end, *value, *value_end;
+  DIR *dir;
 
-
   #ifdef TUXMATH_DEBUG
   printf("\nEntering read_config_file()\n");
   #endif
@@ -1391,10 +1397,13 @@
       if (file_type == GLOBAL_CONFIG_FILE && user_data_dir == NULL)
       {
 	/* Check to see whether the specified homedir exists */
-	if (opendir(value) == NULL)
+	dir = opendir(value);
+	if (dir == NULL)
 	  fprintf(stderr,"homedir: %s is not a directory, or it could not be read\n", value);
-	else
+	else {
 	  set_user_data_dir(value);  /* copy the homedir setting */
+	  closedir(dir);
+	}
       }
     }
 
@@ -2661,7 +2670,15 @@
     fprintf(fp, "************************\n"
                 "* Tuxmath Game Summary *\n"
                 "************************\n");
-    fprintf(fp, "\nPlayer: %s\n", getenv("USER"));
+    if (add_subdir)
+    {
+      /* Identify user by login if we're not in a multiuser configuration */
+      fprintf(fp, "\nPlayer: %s\n", getenv("USER"));
+    }
+    else {
+      /* Identify user by the directory name.*/
+      fprintf(fp, "\nPlayer: %s\n", get_user_name());
+    }
 
     /* Write question list:  */
     fprintf(fp, "\nStarting Question List:");
@@ -2713,6 +2730,8 @@
     else
       fprintf(fp, "Percent Correct: (not applicable)\n");
 
+    fprintf(fp,"Median Time/Question:\t%g\n",MC_MedianTimePerQuestion());
+
     fprintf(fp, "Mission Accomplished:\t");
     if (MC_MissionAccomplished())
     {
@@ -2722,10 +2741,13 @@
     {
       fprintf(fp, "No.\n\n:^(\n");
     }
+
+    fclose(fp);
     return 1;
   }
   else /* Couldn't write file for some reason: */
   {
+    fprintf(stderr,"Summary not written.\n");
     return 0;
   }
 }
@@ -2881,7 +2903,28 @@
   dirname[len] = '\0';
 }
 
+/* Identify user by the directory name. We don't want to use the */
+/* whole path, just the name of the last subdirectory. */
+static char* get_user_name(void)
+{
+  char filepath2[PATH_MAX];
+  char *username;
 
+  get_user_data_dir_with_subdir(filepath2);
+  username = &filepath2[strlen(filepath2)-1];
+  /* Chop off trailing "/" */
+  while (username > &filepath2[0] && *username == '/') {
+    *username = '\0';
+    username--;
+  }
+  /* Back up to the next "/" */
+  while (username > &filepath2[0] && *username != '/')
+    username--;
+
+  return username;
+}
+
+
 /* Allows use of "true", "YES", T, etc. in text file for boolean values. */
 /* Return value of -1 means value string is not recognized.              */
 static int str_to_bool(const char* val)

Modified: tuxmath/trunk/src/game.c
===================================================================
--- tuxmath/trunk/src/game.c	2007-12-28 17:07:28 UTC (rev 395)
+++ tuxmath/trunk/src/game.c	2007-12-30 13:00:46 UTC (rev 396)
@@ -75,6 +75,7 @@
   int bonus;
   int zapped;
   MC_FlashCard flashcard;
+  Uint32 time_started;
 } comet_type;
 
 /* Local (to game.c) 'globals': */
@@ -1022,6 +1023,7 @@
 void game_handle_answer(void)
 {
   int i, num, lowest, lowest_y;
+  Uint32 ctime;
 
   if (!doing_answer)
   {
@@ -1061,6 +1063,14 @@
   {
     MC_AnsweredCorrectly(&(comets[lowest].flashcard));
 
+    /* Store the time the question was present on screen (do this */
+    /* in a way that avoids storing it if the time wrapped around */
+    ctime = SDL_GetTicks();
+    if (ctime > comets[lowest].time_started) {
+      MC_AddTimeToList((float)(ctime - comets[lowest].time_started)/1000);
+    }
+    
+
     /* Destroy comet: */
     comets[lowest].expl = COMET_EXPL_START;
     comets[lowest].zapped = 1;
@@ -1193,6 +1203,7 @@
      changes in the cities, we set some flags in them, too. */
   int i, this_city;
   num_comets_alive = 0;
+  Uint32 ctime;
       
   /* Clear the threatened flag on each city */
   for (i = 0; i < NUM_CITIES; i++)
@@ -1228,7 +1239,14 @@
         /* Tell MathCards about it - question not answered correctly: */
         MC_NotAnsweredCorrectly(&(comets[i].flashcard));
 
-        /* Record data for feedback */
+	/* Store the time the question was present on screen (do this */
+	/* in a way that avoids storing it if the time wrapped around */
+	ctime = SDL_GetTicks();
+	if (ctime > comets[i].time_started) {
+	  MC_AddTimeToList((float)(ctime - comets[i].time_started)/1000);
+	}
+
+        /* Record data for speed feedback */
 	/* Do this only for cities that are alive; dead cities */
         /* might not get much protection from the player */
 	if (Opts_UseFeedback() && cities[this_city].hits_left) {
@@ -1611,7 +1629,7 @@
 void game_handle_extra_life(void)
 {
   // This handles the animation sequence during the rebuilding of an igloo
-  int i,igloo_top,num_below_igloo,status,direction;
+  int i,igloo_top,num_below_igloo,direction;
 
   if (cloud.status == EXTRA_LIFE_ON) {
 
@@ -2415,6 +2433,9 @@
   printf ("\nadd_comet(): formula string is: %s", comets[found].flashcard.formula_string);
   #endif
 
+  /* Record the time at which this comet was created */
+  comets[found].time_started = SDL_GetTicks();
+
   /* comet slot found and question found so return successfully: */
   return 1;
 }

Modified: tuxmath/trunk/src/mathcards.c
===================================================================
--- tuxmath/trunk/src/mathcards.c	2007-12-28 17:07:28 UTC (rev 395)
+++ tuxmath/trunk/src/mathcards.c	2007-12-30 13:00:46 UTC (rev 396)
@@ -33,6 +33,11 @@
 int unanswered = 0;
 int starting_length = 0;
 
+/* For keeping track of timing data */
+float* time_per_question_list = NULL;
+int length_time_per_question_list = 0;
+int length_alloc_time_per_question_list = 0;
+
 /* "private" function prototypes:                        */
 /*                                                       */
 /* these are for internal use by MathCards only - like   */
@@ -61,6 +66,7 @@
 static int sane_value(int i);
 static int abs_value(int i);
 static int randomly_keep(void);
+static int floatCompare(const void *v1,const void *v2);
 
 static void print_list(FILE* fp,MC_MathQuestion* list);
 void print_vect_list(FILE* fp, MC_MathQuestion** vect, int length);
@@ -226,6 +232,14 @@
   delete_list(wrong_quests);
   wrong_quests = NULL;
 
+  /* clear the time list */
+  if (time_per_question_list != NULL) {
+    free(time_per_question_list);
+    time_per_question_list = NULL;
+    length_time_per_question_list = 0;
+    length_alloc_time_per_question_list = 0;
+  }
+
   /* set up new list with pointer to top: */
   question_list = generate_list();
 
@@ -577,6 +591,41 @@
 }
 
 
+/*  Store the amount of time a given flashcard was      */
+/*  visible on the screen. Returns 1 if the request     */
+/*  succeeds, 0 otherwise.                              */
+int MC_AddTimeToList(float t)
+{
+  int newsize = 0;
+  float *newlist;
+
+  /* This list will be allocated in an STL-like manner: when the       */
+  /* list gets full, allocate an additional amount of storage equal    */
+  /* to the current size of the list, so that only O(logN) allocations */
+  /* will ever be needed. We therefore have to keep track of 2 sizes:  */
+  /* the allocated size, and the actual number of items currently on   */
+  /* the list.                                                         */
+  if (length_time_per_question_list >= length_alloc_time_per_question_list) {
+    /* The list is full, allocate more space */
+    newsize = 2*length_time_per_question_list;
+    if (newsize == 0)
+      newsize = 100;
+    newlist = realloc(time_per_question_list,newsize*sizeof(float));
+    if (newlist == NULL) {
+      #ifdef MC_DEBUG
+      printf("\nError: allocation for time_per_question_list failed\n");
+      #endif
+      return 0;
+    }
+    time_per_question_list = newlist;
+    length_alloc_time_per_question_list = newsize;
+  }
+
+  /* Append the time to the list */
+  time_per_question_list[length_time_per_question_list++] = t;
+  return 1;
+}
+
 /* Frees heap memory used in program:                   */
 void MC_EndGame(void)
 {
@@ -591,6 +640,11 @@
     math_opts = 0;
   }
 
+  free(time_per_question_list);
+  time_per_question_list = NULL;
+  length_alloc_time_per_question_list = 0;
+  length_time_per_question_list = 0;
+
   initialized = 0;
 }
 
@@ -1981,6 +2035,16 @@
 }
 
 
+/* Report the median time per question */
+float MC_MedianTimePerQuestion(void)
+{
+  if (length_time_per_question_list == 0)
+    return 0;
+
+  qsort(time_per_question_list,length_time_per_question_list,sizeof(float),floatCompare);
+  return time_per_question_list[length_time_per_question_list/2];
+}
+
 /* Implementation of "private methods" - (cannot be called from outside
 of this file) */
 
@@ -2607,6 +2671,7 @@
           MC_ANSWER_LEN);
   copy->next = original->next;
   copy->previous = original->previous;
+  copy->randomizer = original->randomizer;
   return 1;
 }
 
@@ -2748,7 +2813,7 @@
   }
 
   fprintf(fp, "%s\t", ptr->card.formula_string);
-  fprintf(fp, "randomizer = %d\n", ptr->randomizer);
+  /*fprintf(fp, "randomizer = %d\n", ptr->randomizer);*/
 }  
 
 
@@ -2864,7 +2929,8 @@
 MC_MathQuestion* new_randomize_list(MC_MathQuestion* old_list)
 {
   MC_MathQuestion* old_tmp = old_list;
-  MC_MathQuestion** tmp_vect = NULL; 
+  MC_MathQuestion** tmp_vect = NULL;
+  MC_MathQuestion* new_list_head = NULL;
 
   int i = 0;
   int old_length = list_length(old_list);
@@ -2915,11 +2981,15 @@
       return 0;
     }
     tmp_vect[i]->next = tmp_vect[i+1];
+    tmp_vect[i+1]->previous = tmp_vect[i];
   }
+  tmp_vect[0]->previous = NULL;
+  tmp_vect[old_length-1]->next = NULL;
 
-  tmp_vect[old_length-1]->next = NULL;
   /* Now just return pointer to first element! */
-  return tmp_vect[0];
+  new_list_head = tmp_vect[0];
+  free(tmp_vect);
+  return new_list_head;
 }
 
 /* This is needed for qsort(): */
@@ -3050,3 +3120,19 @@
   else
     return 0;
 }
+
+/* Compares two floats (needed for sorting in MC_MedianTimePerQuestion) */
+int floatCompare(const void *v1,const void *v2)
+{
+  float f1,f2;
+
+  f1 = *((float *) v1);
+  f2 = *((float *) v2);
+
+  if (f1 < f2)
+    return -1;
+  else if (f1 > f2)
+    return 1;
+  else
+    return 0;
+}

Modified: tuxmath/trunk/src/mathcards.h
===================================================================
--- tuxmath/trunk/src/mathcards.h	2007-12-28 17:07:28 UTC (rev 395)
+++ tuxmath/trunk/src/mathcards.h	2007-12-30 13:00:46 UTC (rev 396)
@@ -248,6 +248,13 @@
 /*  including questions currently "in play".              */
 int MC_ListQuestionsLeft(void);
 
+/*  To keep track of how long students take to answer the */
+/*  questions, one can report the time needed to answer   */
+/*  an individual question:                               */
+int MC_AddTimeToList(float t);
+/*  Note that initialization of the list is handled by    */
+/*  MC_StartGame.                                         */
+
 /*  Tells MathCards to clean up - should be called when   */
 /*  user interface program exits.                         */
 void MC_EndGame(void);
@@ -265,6 +272,7 @@
 int MC_WrongListLength(void);
 int MC_NumAnsweredCorrectly(void);
 int MC_NumNotAnsweredCorrectly(void);
+float MC_MedianTimePerQuestion(void);
 
 /* Simple "Set/Get" type functions for option parameters: */
 




More information about the Tux4kids-commits mailing list