[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