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

dbruce at alioth.debian.org dbruce at alioth.debian.org
Thu Mar 8 21:07:06 CET 2007


Author: dbruce
Date: 2006-09-11 10:27:22 +0000 (Mon, 11 Sep 2006)
New Revision: 27

Modified:
   tuxmath/trunk/src/fileops.c
   tuxmath/trunk/src/game.c
   tuxmath/trunk/src/mathcards.c
   tuxmath/trunk/src/mathcards.h
   tuxmath/trunk/src/setup.c
   tuxmath/trunk/src/tuxmath.h
Log:
feedback patch


Modified: tuxmath/trunk/src/fileops.c
===================================================================
--- tuxmath/trunk/src/fileops.c	2006-09-07 20:40:14 UTC (rev 26)
+++ tuxmath/trunk/src/fileops.c	2006-09-11 10:27:22 UTC (rev 27)
@@ -464,8 +464,65 @@
     else if(0 == strcasecmp(parameter, "speed"))
     {
       game_options->speed = atof(value);
+      if (game_options->speed < MINIMUM_SPEED)
+      {
+        game_options->speed = MINIMUM_SPEED;
+        fprintf(stderr,"Warning: speed set below minimum, setting to %g.\n",MINIMUM_SPEED);
+      }
     }
 
+    else if(0 == strcasecmp(parameter, "use_feedback"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->use_feedback = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "danger_level"))
+    {
+      game_options->danger_level = atof(value);
+      if (game_options->danger_level < 0)
+      {
+        game_options->danger_level = 0;
+	fprintf(stderr,"Warning: danger level set below minimum, setting to 0.\n");
+      }
+      if (game_options->danger_level > 1)
+      {
+        game_options->danger_level = 1;
+        fprintf(stderr,"Warning: danger level set above maximum, setting to 1.\n");
+      }
+    }
+ 
+    else if(0 == strcasecmp(parameter, "danger_level_speedup"))
+    {
+      game_options->danger_level_speedup = atof(value);
+      if (game_options->danger_level_speedup < 1)
+      {
+        game_options->danger_level_speedup = 1;
+        fprintf(stderr,"Warning: danger_level_speedup set below minimum, setting to 1.\n");
+      }
+    }
+
+    else if(0 == strcasecmp(parameter, "danger_level_max"))
+    {
+      game_options->danger_level_max = atof(value);
+      if (game_options->danger_level_max > 1)
+      {
+        game_options->danger_level_max = 1;
+        fprintf(stderr,"Warning: danger_level_max set above maximum, setting to 1.\n");
+      }
+    }
+
+    else if(0 == strcasecmp(parameter, "city_explode_handicap"))
+    {
+      game_options->city_expl_handicap = atof(value);
+      if (game_options->city_expl_handicap < 0)
+      {
+        game_options->city_expl_handicap = 0;
+        fprintf(stderr,"Warning: city_explode_handicap leve set below minimum, setting to 0.\n");
+      }
+    }
+
     else if(0 == strcasecmp(parameter, "allow_speedup"))
     {
       int v = str_to_bool(value);
@@ -1026,22 +1083,59 @@
   if(verbose)
   {
     fprintf (fp, "\n############################################################\n" 
-                 "# The remaining settings determine the speed and number    #\n"
+                 "# The next settings determine the speed and number         #\n"
                  "# of comets.  The speed settings are float numbers (mean-  #\n"
                  "# ing decimals allowed). The comet settings are integers.  #\n"
+                 "#                                                          #\n"
+                 "# Starting comet speed and max comet speed are generally   #\n"
+                 "# applicable. The main choice is whether you want to use   #\n"
+                 "# feedback, i.e., to adjust the speed automatically based  #\n"
+                 "# on the player's performance.                             #\n"
+                 "#                                                          #\n"
+                 "# Without feedback, the speed increases by a user-         #\n"
+                 "# settable factor ('speedup_factor'), with an option       #\n"
+                 "# ('slow_after_wrong') to go back to the starting speed    #\n"
+                 "# when a city gets hit.                                    #\n"
+                 "#                                                          #\n"
+                 "# With feedback, you set a desired 'danger level,' which   #\n"
+                 "# determines how close the comets should typically         #\n"
+                 "# approach the cities before the player succeeds in        #\n"
+                 "# destroying them.  The game will adjust its speed         #\n"
+                 "# accordingly, getting faster when the player is easily    #\n"
+                 "# stopping the comets, and slowing down when there are     #\n"
+                 "# too many close calls or hits. You can also have the      #\n"
+                 "# danger level increase with each wave.                    #\n"
                  "############################################################\n");
   }
 
   if(verbose)
   {
+    fprintf (fp, "\n# Whether to increase speed and number of comets with \n"
+                 "# each wave.  May want to turn this off for smaller kids.\n"
+                 "# Default is 1 (allow game to speed up)\n");
+  }
+  fprintf(fp, "allow_speedup = %d\n", game_options->allow_speedup);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# This option tells Tuxmath to go back to starting speed \n"
+                 "# and number of comets if the player misses a question\n"
+                 "# Useful for smaller kids. Default is 0.\n");
+  }
+  fprintf(fp, "slow_after_wrong = %d\n", game_options->slow_after_wrong);
+
+  if(verbose)
+  {
     fprintf (fp, "\n# Starting comet speed. Default is 1.\n");
   }
   fprintf(fp, "speed = %f\n", game_options->speed);
 
   if(verbose)
   {
-    fprintf (fp, "\n# Speed is multiplied by this factor with each new wave.\n"
-                 "# Default is 1.2\n");
+    fprintf (fp, "\n# If feedback is not used but 'allow_speedup' is\n"
+                 "# enabled, the comet speed will be\n"
+                 "# multiplied by this factor with each new wave.\n"
+                 "# Default is 1.2 (i.e. 20 percent increase per wave)\n");
   }
   fprintf(fp, "speedup_factor = %f\n", game_options->speedup_factor);
 
@@ -1071,19 +1165,61 @@
 
   if(verbose)
   {
-    fprintf (fp, "\n# Allow speed and number of comets to increase with each\n"
-                 "# wave.  May want to turn this off for smaller kids.\n"
-                 "Default is 1 (allow game to speed up)\n");
+     fprintf (fp, "\n# Use feedback? Default (for now) is false, 0.\n");
   }
-  fprintf(fp, "allow_speedup = %d\n", game_options->allow_speedup);
+  fprintf(fp, "use_feedback = %d\n", game_options->use_feedback);
 
   if(verbose)
   {
-    fprintf (fp, "\n# Go back to starting speed and number of comets if player\n"
-                 "# misses a question. Useful for smaller kids. Default is 0.\n");
+    fprintf (fp, "\n# (Non-feedback) Speed is multiplied by this factor\n"
+                 "# with each new wave. Default is 1.2.\n");
   }
-  fprintf(fp, "slow_after_wrong = %d\n", game_options->slow_after_wrong);
+  fprintf(fp, "speedup_factor = %f\n", game_options->speedup_factor);
 
+  if(verbose)
+  {
+    fprintf (fp, "\n# Go back to starting speed and number of comets if player\n"
+                  "# misses a question. Useful for smaller kids. Default is 0.\n");
+   }
+   fprintf(fp, "slow_after_wrong = %d\n", game_options->slow_after_wrong);
+
+   if(verbose)
+   {
+     fprintf (fp, "\n# (Feedback) Set the desired danger level.\n"
+             "# 0 = too safe, comets typically exploded right at the very top\n"
+             "# 1 = too dangerous, comets typically exploded at the moment they hit cities\n"
+             "# Set it somewhere between these extremes. As a guideline, early\n"
+             "# elementary kids might feel comfortable around 0.2-0.3, older kids\n"
+             "# at around 0.4-0.6. Default 0.35.\n");
+   }
+   fprintf(fp, "danger_level = %f\n", game_options->danger_level);
+
+   if(verbose)
+   {
+     fprintf (fp, "\n# (Feedback) Set danger level speedup.\n"
+                  "# The margin of safety will decrease by this factor each wave.\n"
+                  "# Default 1.1. Note 1 = no increase in danger level.\n");
+   }
+   fprintf(fp, "danger_level_speedup = %f\n", game_options->danger_level_speedup);
+
+   if(verbose)
+   {
+     fprintf (fp, "\n# (Feedback) Set the maximum danger level.\n"
+                  "# Default 0.9.\n");
+   }
+   fprintf(fp, "danger_level_max = %f\n", game_options->danger_level_max);
+
+   if (verbose)
+   { 
+     fprintf (fp, "\n# (Feedback) Set the handicap for hitting cities.\n"
+                  "# When bigger than 0, this causes the game to slow down\n"
+                  "# by an extra amount after a wave in which one or more\n"
+                  "# cities get hit. Note that this is similar to slow_after_wrong,\n"
+                  "# but allows for more gradual changes.\n"
+                  "# Default 0 (no extra handicap).\n");
+   }
+   fprintf(fp, "city_explode_handicap = %f\n", game_options->city_expl_handicap);
+
 /*
   fprintf(fp, "num_cities = %d\n", game_options->num_cities);
   fprintf(fp, "num_bkgds = %d\n", game_options->num_bkgds);

Modified: tuxmath/trunk/src/game.c
===================================================================
--- tuxmath/trunk/src/game.c	2006-09-07 20:40:14 UTC (rev 26)
+++ tuxmath/trunk/src/game.c	2006-09-11 10:27:22 UTC (rev 27)
@@ -100,6 +100,13 @@
 static int level_start_wait;
 static int last_bkgd;
 
+/* Feedback-related variables */
+static int city_expl_height;
+static int comet_feedback_number;
+static float comet_feedback_height;
+static float danger_level;
+
+
 static int digits[3];
 static comet_type comets[MAX_COMETS];
 static city_type cities[NUM_CITIES];
@@ -411,6 +418,13 @@
   }  
   
   /* Prepare to start the game: */
+
+  city_expl_height = screen->h - images[IMG_CITY_BLUE]->h;
+
+  /* Initialize feedback parameters */
+  comet_feedback_number = 0;
+  comet_feedback_height = 0;
+  danger_level = game_options->danger_level;
   
   wave = 1;
   num_attackers = 2;
@@ -620,6 +634,18 @@
     laser.y2 = comets[lowest].y;
     playsound(SND_LASER);
     playsound(SND_SIZZLE);
+
+    /* Record data for feedback */
+    if (game_options->use_feedback)
+    {
+      comet_feedback_number++;
+      comet_feedback_height += comets[lowest].y/city_expl_height;
+
+      #ifdef FEEDBACK_DEBUG
+      printf("Added comet feedback with height %g\n",comets[lowest].y/city_expl_height);
+      #endif
+    }
+
 	    
     /* FIXME maybe should move this into game_handle_tux() */
     /* 50% of the time.. */
@@ -734,15 +760,16 @@
     if (comets[i].alive)
     {
       num_comets_alive++;
-      /* update comet position */
+      /* Update comet position */
       comets[i].x = comets[i].x + 0; /* no lateral motion for now! */
       comets[i].y = comets[i].y + (speed);
-	      
-      if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
+
+      /* Did it hit a city? */
+      if (comets[i].y >= city_expl_height &&
 	  comets[i].expl < COMET_EXPL_END)
       {
-        /* Tell MathCards about it: */
-        MC_AnsweredIncorrectly(&(comets[i].flashcard));
+        /* Tell MathCards about it - question not answered correctly: */
+        MC_NotAnsweredCorrectly(&(comets[i].flashcard));
 
         /* Disable shields or destroy city: */
         if (cities[comets[i].city].shields)
@@ -756,7 +783,19 @@
           playsound(SND_EXPLOSION);
         }
 
-        /* If slow_after_wrong selected, set flag to go back to starting speed and */
+        /* Record data for feedback */
+	/* Do this only for cities that are threatened; dead cities */
+        /* might not get much protection from the player */
+	if (game_options->use_feedback && cities[comets[i].city].alive) {
+	  comet_feedback_number++;
+          comet_feedback_height += 1.0 + game_options->city_expl_handicap;
+
+          #ifdef FEEDBACK_DEBUG
+ 	  printf("Added comet feedback with height %g\n",1.0 + game_options->city_expl_handicap);
+ 	  #endif
+ 	}
+ 
+       /* If slow_after_wrong selected, set flag to go back to starting speed and */
         /* number of attacking comets: */
         if (game_options->slow_after_wrong)
         {
@@ -1146,7 +1185,10 @@
 void reset_level(void)
 {
   char fname[1024];
-  int i;
+  int i;    
+  int next_wave_comets;
+  int use_feedback;
+  float comet_avg_height,height_differential;
   
   
   /* Clear all comets: */
@@ -1197,6 +1239,7 @@
   }
 
 
+
   /* Record score before this wave: */
 
   pre_wave_score = score;
@@ -1206,25 +1249,98 @@
   /* On first wave or if slowdown flagged due to wrong answer: */
   if (wave == 1 || slowdown)
   {
-    prev_wave_comets = game_options->starting_comets;
+    next_wave_comets = game_options->starting_comets;
+
+    /* NOTE: not sure this really goes here: */
+    prev_wave_comets = next_wave_comets;
+    comet_feedback_number = 0;
+    comet_feedback_height = 0;
+
     speed = game_options->speed;
     slowdown = 0;
   }
+
   else /* Otherwise increase comets and speed if selected, not to */
        /* exceed maximum:                                         */
   {
+    next_wave_comets = prev_wave_comets; /* here's the important fix */
     if (game_options->allow_speedup)
     {
-      prev_wave_comets += game_options->extra_comets_per_wave;
-      if (prev_wave_comets > game_options->max_comets)
+      next_wave_comets += game_options->extra_comets_per_wave;
+      if (next_wave_comets > game_options->max_comets)
       {
-        prev_wave_comets = game_options->max_comets;
-      } 
-      speed = speed * game_options->speedup_factor;
-      if (speed > game_options->max_speed)
+        next_wave_comets = game_options->max_comets;
+      }
+      
+      use_feedback = game_options->use_feedback;
+
+      if (use_feedback) 
       {
-        speed = game_options->max_speed;
-      } 
+	#ifdef FEEDBACK_DEBUG
+	printf("Evaluating feedback...\n  old danger level = %g,",danger_level);
+        #endif
+	
+        /* Update our danger level, i.e., the target height */
+	danger_level = 1 - (1-danger_level) / 
+	                   game_options->danger_level_speedup;
+	if (danger_level > game_options->danger_level_max)
+	  danger_level = game_options->danger_level_max;
+
+	#ifdef FEEDBACK_DEBUG
+	printf(" new danger level = %g.\n",danger_level);
+	#endif
+
+	/* Check to see whether we have any feedback data. If not, skip it. */
+	if (comet_feedback_number == 0)
+        {
+	  use_feedback = 0;  /* No comets above living cities, skip feedback */
+
+	  #ifdef FEEDBACK_DEBUG
+	  printf("No feedback data available, aborting.\n\n");
+	  #endif
+	}
+	else 
+        {
+	  /* Compute the average height of comet destruction. */
+	  comet_avg_height = comet_feedback_height/comet_feedback_number;
+
+	  /* Determine how this average height compares with target. */
+	  height_differential = comet_avg_height - danger_level;
+
+	  /* Set the speed so that we move halfway towards the target */
+	  /* height. That makes the changes a bit more conservative. */
+
+	  #ifdef FEEDBACK_DEBUG
+	  printf("  comet average height = %g, height differential = %g.\n", 
+                 comet_avg_height, height_differential);
+	  printf("  old speed = %g,",speed);
+	  #endif
+
+	  speed *= (1 - height_differential/danger_level/2);
+
+	  /* Enforce bounds on speed */
+	  if (speed < MINIMUM_SPEED)
+	    speed = MINIMUM_SPEED;
+	  if (speed > game_options->max_speed)
+	    speed = game_options->max_speed;
+
+	  #ifdef FEEDBACK_DEBUG
+	  printf(" new speed = %g.\n",speed);
+	  printf("Feedback evaluation complete.\n\n");
+	  #endif
+	}
+      }
+
+      if (!use_feedback)
+      {
+        /* This is not an "else" because we might skip feedback */
+	/* when comet_feedback_number == 0 */
+	speed = speed * game_options->speedup_factor;
+	if (speed > game_options->max_speed)
+	{
+	  speed = game_options->max_speed;
+	}
+      }
     }
   }
   num_attackers = prev_wave_comets;

Modified: tuxmath/trunk/src/mathcards.c
===================================================================
--- tuxmath/trunk/src/mathcards.c	2006-09-07 20:40:14 UTC (rev 26)
+++ tuxmath/trunk/src/mathcards.c	2006-09-11 10:27:22 UTC (rev 27)
@@ -43,7 +43,7 @@
 static void clear_negatives(void);
 static int validate_question(int n1, int n2, int n3);
 static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
-static MC_MathQuestion* create_node_from_card(MC_FlashCard* card);
+static MC_MathQuestion* create_node_from_card(MC_FlashCard* flashcard);
 static MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node);
 static MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node);
 static MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n);
@@ -442,24 +442,24 @@
   return 1;
 }
 
-/*  MC_AnsweredIncorrectly() is how the user interface    */
+/*  MC_NotAnsweredCorrectly() is how the user interface    */
 /*  tells MathCards that the player failed to answer the  */
 /*  question correctly. Returns 1 if no errors.           */
 /*  Note: this gets triggered only if a player's city     */
 /*  gets hit by a question, not if they "miss".           */
-int MC_AnsweredIncorrectly(MC_FlashCard* fc)
+int MC_NotAnsweredCorrectly(MC_FlashCard* fc)
 {
   #ifdef MC_DEBUG
-  printf("\nEntering MC_AnsweredIncorrectly()");
+  printf("\nEntering MC_NotAnsweredCorrectly()");
   #endif
 
   if (!fc)
   {
-    fprintf(stderr, "\nMC_AnsweredIncorrectly() passed invalid pointer as argument!\n");
+    fprintf(stderr, "\nMC_NotAnsweredCorrectly() passed invalid pointer as argument!\n");
 
     #ifdef MC_DEBUG
     printf("\nInvalid MC_FlashCard* argument!");
-    printf("\nLeaving MC_AnsweredIncorrectly()\n");
+    printf("\nLeaving MC_NotAnsweredCorrectly()\n");
     #endif
 
     return 0;
@@ -516,7 +516,7 @@
   else
   {
     #ifdef MC_DEBUG
-    printf("\nnot repeating wrong answers\n");
+    printf("\nNot repeating wrong answers\n");
     #endif
 
     /* not repeating questions so list gets shorter:      */
@@ -525,7 +525,7 @@
 
   #ifdef MC_DEBUG
   print_counters();
-  printf("\nLeaving MC_Answered_Incorrectly()\n");
+  printf("\nLeaving MC_NotAnswered_Correctly()\n");
   #endif
 
   return 1;
@@ -2075,21 +2075,13 @@
 }
 #endif
 
-/* FIXME take care of strings */
 MC_MathQuestion* create_node_from_card(MC_FlashCard* flashcard)
 {
-  MC_MathQuestion* ptr;
-  if (!flashcard)
-    return 0;
-  ptr = malloc(sizeof(MC_MathQuestion));
-  ptr->card.num1 = flashcard->num1;
-  ptr->card.num2 = flashcard->num2;
-  ptr->card.num3 = flashcard->num3;
-  ptr->card.operation = flashcard->operation;
-  ptr->card.format = flashcard->format;
-  ptr->next = 0;
-  ptr->previous = 0;
-  return ptr;
+  return create_node(flashcard->num1,
+                     flashcard->num2,
+                     flashcard->operation,
+                     flashcard->num3,
+                     flashcard->format);
 }
 
 #ifdef MC_DEBUG

Modified: tuxmath/trunk/src/mathcards.h
===================================================================
--- tuxmath/trunk/src/mathcards.h	2006-09-07 20:40:14 UTC (rev 26)
+++ tuxmath/trunk/src/mathcards.h	2006-09-11 10:27:22 UTC (rev 27)
@@ -213,10 +213,10 @@
 /*  correctly. Returns 1 if no errors.                    */
 int MC_AnsweredCorrectly(MC_FlashCard* q);
 
-/*  MC_AnsweredIncorrectly() is how the user interface    */
+/*  MC_NotAnsweredCorrectly() is how the user interface    */
 /*  tells MathCards that the question has not been        */
 /*  answered correctly. Returns 1 if no errors.           */
-int MC_AnsweredIncorrectly(MC_FlashCard* q);
+int MC_NotAnsweredCorrectly(MC_FlashCard* q);
 
 /*  Like MC_NextQuestion(), but takes "flashcard" from    */
 /*  pile of incorrectly answered questions.               */

Modified: tuxmath/trunk/src/setup.c
===================================================================
--- tuxmath/trunk/src/setup.c	2006-09-07 20:40:14 UTC (rev 26)
+++ tuxmath/trunk/src/setup.c	2006-09-11 10:27:22 UTC (rev 27)
@@ -541,6 +541,12 @@
   game_options->extra_comets_per_wave = DEFAULT_EXTRA_COMETS_PER_WAVE;
   game_options->max_comets = DEFAULT_MAX_COMETS;
   game_options->sound_available = DEFAULT_SOUND_AVAILABLE;
+  game_options->use_feedback = DEFAULT_USE_FEEDBACK;
+  game_options->danger_level = DEFAULT_DANGER_LEVEL;
+  game_options->danger_level_speedup = DEFAULT_DANGER_LEVEL_SPEEDUP;
+  game_options->danger_level_max = DEFAULT_DANGER_LEVEL_MAX;
+  game_options->city_expl_handicap = DEFAULT_CITY_EXPL_HANDICAP;
+
   game_options->num_cities = DEFAULT_NUM_CITIES;   /* MUST BE AN EVEN NUMBER! */
   game_options->num_bkgds = DEFAULT_NUM_BKGDS;
   game_options->max_city_colors = DEFAULT_MAX_CITY_COLORS;

Modified: tuxmath/trunk/src/tuxmath.h
===================================================================
--- tuxmath/trunk/src/tuxmath.h	2006-09-07 20:40:14 UTC (rev 26)
+++ tuxmath/trunk/src/tuxmath.h	2006-09-11 10:27:22 UTC (rev 27)
@@ -30,6 +30,8 @@
 
 //#define NOSOUND
 //#define TUXMATH_DEBUG   /* for conditional compilation of debugging output */
+//#define FEEDBACK_DEBUG  /* for Tim's feedback speed control code           */
+
 #define TUXMATH_VERSION 0.93
 
 #define PATH_MAX 4096
@@ -54,7 +56,13 @@
 #define DEFAULT_NUM_CITIES 4   /* MUST BE AN EVEN NUMBER! */
 #define DEFAULT_NUM_BKGDS 5
 #define DEFAULT_MAX_CITY_COLORS 4
+#define DEFAULT_USE_FEEDBACK 0
+#define DEFAULT_DANGER_LEVEL 0.35
+#define DEFAULT_DANGER_LEVEL_SPEEDUP 1.1
+#define DEFAULT_DANGER_LEVEL_MAX 0.9
+#define DEFAULT_CITY_EXPL_HANDICAP 0
 
+#define MINIMUM_SPEED 0.1
 
 /* this struct contains all options regarding general       */
 /* gameplay but not having to do with math questions per se */
@@ -76,6 +84,12 @@
   int extra_comets_per_wave;
   int max_comets;  
   char next_mission[PATH_MAX];
+  int use_feedback;
+  float danger_level;
+  float danger_level_speedup;
+  float danger_level_max;
+  float city_expl_handicap;
+
   /* whether sound system is successfully initialized and sound files loaded: */
   /* this flag is set by the program, not the user, and is not in the config file. */
   int sound_available;




More information about the Tux4kids-commits mailing list