[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