[Tux4kids-commits] r10 - in tuxmath/trunk: . docs src

dbruce at alioth.debian.org dbruce at alioth.debian.org
Thu Mar 8 20:58:32 CET 2007


Author: dbruce
Date: 2006-05-16 17:05:20 +0000 (Tue, 16 May 2006)
New Revision: 10

Added:
   tuxmath/trunk/src/mathcards.c
   tuxmath/trunk/src/mathcards.h
Modified:
   tuxmath/trunk/Makefile
   tuxmath/trunk/docs/CHANGES.txt
   tuxmath/trunk/src/game.c
   tuxmath/trunk/src/game.h
   tuxmath/trunk/src/options.c
   tuxmath/trunk/src/setup.c
   tuxmath/trunk/src/tuxmath.c
   tuxmath/trunk/src/tuxmath.h
Log:
Incorporated MathCards backend.


Modified: tuxmath/trunk/Makefile
===================================================================
--- tuxmath/trunk/Makefile	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/Makefile	2006-05-16 17:05:20 UTC (rev 10)
@@ -61,7 +61,8 @@
 
 
 tuxmath:	obj/tuxmath.o obj/setup.o obj/title.o obj/game.o \
-		obj/options.o obj/credits.o obj/playsound.o
+		obj/options.o obj/credits.o obj/playsound.o \
+                obj/mathcards.o
 	@echo "LINKING!"
 	$(CC) $(CFLAGS) $^ -o tuxmath $(LIBS)
 
@@ -111,3 +112,7 @@
 	-mkdir -p obj
 	$(CC) $(CFLAGS) src/playsound.c -c -o obj/playsound.o
 
+obj/mathcards.o:	src/mathcards.c src/mathcards.h 
+	@echo "BUILDING mathcards.o"
+	-mkdir -p obj
+	$(CC) $(CFLAGS) src/mathcards.c -c -o obj/mathcards.o
\ No newline at end of file

Modified: tuxmath/trunk/docs/CHANGES.txt
===================================================================
--- tuxmath/trunk/docs/CHANGES.txt	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/docs/CHANGES.txt	2006-05-16 17:05:20 UTC (rev 10)
@@ -1,5 +1,31 @@
 CHANGES.txt for "tuxmath"
 
+2006.May.16 (https://svn.tux4kids.net/tuxmath/ - revision 8)
+  Code:
+    * Major changes to internal workings of program. Everything 
+      related to generation of math questions is now handled by
+      a backend called MathCards, which generates question lists
+      based on parameters in a struct called math_opts. MathCards
+      is (obviously) contained in two new files, mathcards.h and
+      mathcards.c.  For now, many options can only be set at 
+      compile time by changing the defaults in mathcards.h. 
+      MathCards allows fine-grained control of the questions to
+      be asked, and allows the player to "win" if all of the
+      questions in the list are answered while the cities are
+      still alive.  By default, game behavior is unchanged from
+      previously.
+    * Main game() function in game.c has been split into several
+      smaller functions; updated to use MathCards. 
+    * Options() updated to use MathCards.
+  Game:
+    * demo mode now handles negative answers properly.
+
+  Note: the code contains many FIXMEs and TODOs and should be
+      tested more before being packaged for a distribution.
+      I would describe revision 8 as a developer or alpha release.
+
+      David Bruce <dbruce at tampabay.rr.com>
+
 2006.Mar.8 (https://svn.tux4kids.net/tuxmath/ - revision 7)
   Setup:
     * updated usage() to include all command-line options

Modified: tuxmath/trunk/src/game.c
===================================================================
--- tuxmath/trunk/src/game.c	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/game.c	2006-05-16 17:05:20 UTC (rev 10)
@@ -30,12 +30,13 @@
 #include "sounds.h"
 #include "playsound.h"
 #include "tuxmath.h"
+#include "mathcards.h"  
 
 #define FPS (1000 / 15)   /* 15 fps max */
 
 #define CITY_EXPL_START 3 * 5  /* Must be mult. of 5 (number of expl frames) */
 #define ANIM_FRAME_START 4 * 2 /* Must be mult. of 2 (number of tux frames) */
-#define GAMEOVER_COUNTER_START 75
+#define GAMEOVER_COUNTER_START 750
 #define LEVEL_START_WAIT_START 20
 #define LASER_START 5
 
@@ -51,17 +52,13 @@
   "addition", "subtraction", "multiplication", "division"
 };
 
-typedef struct range_type {
-  int min;
-  int max;
-} range_type;
-
-static range_type ranges[NUM_Q_RANGES] = {
+range_type ranges[NUM_Q_RANGES] = {
   {0, 5},
   {6, 12},
   {13, 20}
 };
 
+
 #define ANSWER_LEN 5
 #define FORMULA_LEN 8
 typedef struct comet_type {
@@ -73,71 +70,282 @@
   char formula[FORMULA_LEN];
   int answer;
   char answer_str[ANSWER_LEN];
+  MC_FlashCard flashcard;
 } comet_type;
 
 /* Local (to game.c) 'globals': */
 
-static int wave, score, pre_wave_score, num_attackers;
+static int quit;
+static int done;
+static int gameover;
+static int game_status;
+static int SDL_quit_received;
+static int escape_received;
+static int paused;
+static int wave;
+static int score;
+static int pre_wave_score;
+static int num_attackers;
+static int demo_countdown;
+static int tux_anim;
+static int tux_anim_frame;
+static int num_cities_alive;
+static int num_comets_alive;
+static int tux_img;
+static int old_tux_img;
+static int frame;
+static int neg_answer_picked;
+static int tux_pressing;
+static int doing_answer;
+static int level_start_wait;
+static int last_bkgd;
+
+static  Uint32 last_time, now_time;
+
 static int digits[3];
 static comet_type comets[MAX_COMETS];
 static city_type cities[NUM_CITIES];
 static laser_type laser;
-static SDL_Surface * bkgd;
-static int last_bkgd;
+static SDL_Surface* bkgd;
 
-static int neg_answer_picked;
-static int tux_pressing, done, paused, doing_answer;
-static int level_start_wait;
-
 /* Local function prototypes: */
+static int  game_initialize(void);
+static void game_handle_user_events(void);
+static void game_handle_demo(void);
+static void game_handle_answer(void);
+static void game_countdown(void);
+static void game_handle_tux(void);
+static void game_handle_comets(void);
+static void game_handle_cities(void);
+static void game_draw(void);
+static int check_exit_conditions(void);
+static void print_exit_conditions(void);
 
 static void reset_level(void);
-static void add_comet(void);
-static void draw_numbers(char * str, int x);
-static int pause_game(void);
+static int add_comet(void);
+static void draw_numbers(char* str, int x);
+static int  pause_game(void);
 static void draw_line(int x1, int y1, int x2, int y2, int r, int g, int b);
 static void putpixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
 static void draw_console_image(int i);
 static void add_score(int inc);
-static int pick_operand(int min);
-static int in_range(int n);
+static int  pick_operand(int min);
+static int  in_range(int n);
+static void reset_comets(void);
 
 static void draw_led_nums(void);
-static void game_mouse_event(SDL_Event the_event);
-static void game_key_event(SDLKey pressed_key);
+static void game_mouse_event(SDL_Event event);
+static void game_key_event(SDLKey key);
+
 /* --- MAIN GAME FUNCTION!!! --- */
 
-/* FIXME consider breaking this large function up into more managable pieces */
-/* e.g. initialize(), handle_events(), update_data(), draw(), etc            */ 
+
 int game(void)
 {
-  int i, j, num, img, quit, frame, lowest, lowest_y,
-    tux_img, old_tux_img, tux_anim, tux_anim_frame,
-    tux_same_counter, num_cities_alive,
-    num_comets_alive, demo_countdown, picked_comet, answer_digit,
-    gameover;
-  SDL_Event event;
-  Uint32 last_time, now_time;
-  SDLKey key;
-  SDL_Rect src, dest;
-  char str[64];
-  char* comet_str;
 
+   /* most code moved into smaller functions (game_*()): */
+  if (!game_initialize())
+  {
+    printf("\ngame_initialize() failed!");
+    fprintf(stderr, "\ngame_initialze() failed!");
+    done = 1;
+    return 0;
+    /*exit(1);*/    
+  } 
+
+  /* --- MAIN GAME LOOP: --- */ 
+  do
+  {
+    /* reset or increment various things with each loop: */
+    frame++;
+    last_time = SDL_GetTicks();
+    old_tux_img = tux_img;
+    tux_pressing = 0;
+
+    game_handle_user_events();
+     
+    if (doing_answer)
+    {
+      game_handle_answer();
+    }
+
+    if (laser.alive > 0)
+    {
+      laser.alive--;
+    }      
+
+    if (level_start_wait > 0)
+    {
+      game_countdown();
+    }
+     
+    game_handle_tux();
+    game_handle_comets();
+    game_handle_cities();
+
+
+    if (game_options->demo_mode)
+    {
+      game_handle_demo(); 
+    }     
+    /* drawing code moved into own function: */
+    game_draw();
+    
+   
+    /* If we're in "PAUSE" mode, pause! */
+    if (paused)
+    {
+      /*quit = */pause_game();
+      paused = 0;
+    }
+      
+      /* Keep playing music: */
+      
+#ifndef NOSOUND
+    if (game_options->use_sound)
+    {
+      if (!Mix_PlayingMusic())
+      {
+	    Mix_PlayMusic(musics[MUS_GAME + (rand() % 3)], 0);
+      }  
+    }
+#endif
+    /* figure out if we should leave loop: */
+    game_status = check_exit_conditions(); 
+ 
+    /* Pause (keep frame-rate event) */
+    now_time = SDL_GetTicks();
+    if (now_time < last_time + FPS)
+    {
+      SDL_Delay(last_time + FPS - now_time);
+    }
+  }
+  while(GAME_IN_PROGRESS == game_status);
+  /* END OF MAIN GAME LOOP! */
+
+ #ifdef TUXMATH_DEBUG
+   print_exit_conditions();
+  #endif
+
+  /* TODO: handle various end of game scenarios based on value of game_status */
+  switch (game_status)
+  {
+    SDL_Rect dest;
+    SDL_Event event;
+
+    case GAME_OVER_WON:
+    {
+      break;
+    }
+
+    case GAME_OVER_ERROR:
+    {
+      #ifdef TUXMATH_DEBUG
+      printf("\ngame() exiting with error");
+      #endif
+    }
+    case GAME_OVER_LOST: 
+    case GAME_OVER_OTHER:
+    {
+      gameover = 1;
+      do
+      {
+
+        while (SDL_PollEvent(&event) > 0)
+        {
+          if  (event.type == SDL_QUIT
+            || event.type == SDL_KEYDOWN
+            || event.type == SDL_MOUSEBUTTONDOWN)
+          {
+            gameover = 0;
+          }   
+        }
+
+        dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
+        dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
+        dest.w = images[IMG_GAMEOVER]->w;
+        dest.h = images[IMG_GAMEOVER]->h;
+	
+        SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
+        SDL_Flip(screen);
+
+        now_time = SDL_GetTicks();
+
+        if (now_time < last_time + FPS)
+	  SDL_Delay(last_time + FPS - now_time);     
+      }
+      while (gameover);
+
+      break;
+    }
+
+    case GAME_OVER_ESCAPE:
+    {
+      break;
+    }
+
+    case GAME_OVER_WINDOW_CLOSE:
+    {
+      break;
+    }
+
+  } 
+
+  /* Free background: */
+  if (bkgd != NULL)
+  {
+    SDL_FreeSurface(bkgd);
+  }
+
+  /* Stop music: */
+#ifndef NOSOUND
+  if (game_options->use_sound)
+  {
+    if (Mix_PlayingMusic())
+    {
+      Mix_HaltMusic();
+    }
+  }
+#endif
+  
+  /* Return the chosen command: */
+  return quit;
+}
+
+
+
+int game_initialize(void)
+{
+  int i;
   /* Clear window: */
   
   SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
   SDL_Flip(screen);
-  
-  
-  /* --- MAIN GAME LOOP: --- */
 
+  /* FIXME done and quit will be superseded by game_status */
   done = 0;
   quit = 0;
   
+  game_status = GAME_IN_PROGRESS;  
+  SDL_quit_received = 0;
+  escape_received = 0;
+
+  /* Start MathCards backend: */
+  /* FIXME may need to move this into tuxmath.c to accomodate option */
+  /* to us MC_StartUsingWrongs() */
+  if (!MC_StartGame())
+  {
+    printf("\nMC_StartGame() failed!");
+    fprintf(stderr, "\nMC_StartGame() failed!");
+    done = 1;
+    return 0;
+    /*exit(1);*/
+  }  
   
   /* Prepare to start the game: */
   
   wave = 1;
+  num_attackers = 2;
   score = 0;
   gameover = 0;
   demo_countdown = 1000;
@@ -174,547 +382,484 @@
 
 
   /* (Clear laser) */
-
   laser.alive = 0;
-
   
   /* Reset remaining stuff: */
  
   bkgd = NULL;
   last_bkgd = -1;
   reset_level();
-  
-  
-  /* --- MAIN GAME LOOP!!! --- */
-  
+  reset_comets();  
+
   frame = 0;
   paused = 0;
-  picked_comet = -1;
-  answer_digit = 0;
   doing_answer = 0;
+  tux_pressing = 0;
   tux_img = IMG_TUX_RELAX1;
   tux_anim = -1;
   tux_anim_frame = 0;
-  tux_same_counter = 0;
 
-  
-  do
-    {
-      frame++;
-      last_time = SDL_GetTicks();
+  return 1;
+}
 
+void game_handle_user_events(void)
+{
+  SDL_Event event;
+  SDLKey key;
 
-      /* Handle any incoming events: */
-     
-      old_tux_img = tux_img;
-      tux_pressing = 0;
+  while (SDL_PollEvent(&event) > 0)
+  {
+    if (event.type == SDL_QUIT)
+    {
+      SDL_quit_received = 1;
+      /* FIXME quit and done flags will be superseded */
+      /* Window close event - quit! */
+      quit = 1;
+      done = 1;
+    }
+    else if (event.type == SDL_KEYDOWN)
+    {
+      key = event.key.keysym.sym;
+      game_key_event(key);
+    }
+    else if (event.type == SDL_MOUSEBUTTONDOWN)
+    {
+      game_mouse_event(event);
+    }
+  }
+}
 
-      while (SDL_PollEvent(&event) > 0)
-	{
-	  if (event.type == SDL_QUIT)
-	    {
-	      /* Window close event - quit! */
-	      quit = 1;
-	      done = 1;
-	    }
-	  else if (event.type == SDL_KEYDOWN)
-	    {
-	      key = event.key.keysym.sym;
-	      game_key_event(key);
-	    }
-	  else if (event.type == SDL_MOUSEBUTTONDOWN)
-	    {
-              game_mouse_event(event);
-	    }
-	}
+void game_handle_demo(void)
+{
+  /* Demo mode! */
+  int demo_answer, answer_digit;
+  static int picked_comet;
+  if (picked_comet == -1 && (rand() % 10) < 3)
+  {
+    /* Demo mode!  Randomly pick a comet to destroy: */
+    picked_comet = (rand() % MAX_COMETS);
 
+    if (!comets[picked_comet].alive || comets[picked_comet].y < 80)
+    {
+      picked_comet = -1;
+    }
+    else
+    {
+      /* found a comet to blow up! */
+      demo_answer = comets[picked_comet].answer;
+      /* handle negative answer: */
+      if (demo_answer < 0)
+      {
+        demo_answer = -demo_answer;
+        neg_answer_picked = 1;
+      }
+      if (demo_answer >= 100)
+        answer_digit = 0;
+      else if (demo_answer >= 10)
+        answer_digit = 1;
+      else
+        answer_digit = 2;
+    }
+  }
+  
+  /* Add a digit: */
+  if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
+  {
+    tux_pressing = 1;
 
+    if (answer_digit < 3)
+    {
+      digits[0] = digits[1];
+      digits[1] = digits[2];
 
-      if (game_options->demo_mode)
+      if (answer_digit == 0)
       {
-        /* Demo mode! */
-        /* FIXME update this to handle negatives correctly */
+        digits[2] = demo_answer / 100;
+      }
+      else if (answer_digit == 1)
+      {
+        digits[2] = (demo_answer % 100) / 10;
+      }
+      else if (answer_digit == 2)
+      {
+        digits[2] = (demo_answer % 10);
+      }
+	    
+      answer_digit++;
+    }
+    else
+    {
+      /* "Press Return" */
+      doing_answer = 1;
+      picked_comet = -1;
+    }
+  }
 
-        if (picked_comet == -1 && (rand() % 10) < 3)
-        {
-	  /* Demo mode!  Randomly pick a comet to destroy: */
-	
-	  picked_comet = (rand() % MAX_COMETS);
-	
-	  if (!comets[picked_comet].alive || comets[picked_comet].y < 80)
-            picked_comet = -1;
-	  else
-	  {
-	    if (comets[picked_comet].answer >= 100)
-	      answer_digit = 0;
-	    else if (comets[picked_comet].answer >= 10)
-	      answer_digit = 1;
-	    else
-              answer_digit = 2;
-	  }
-        }
-      
+  /* Count down counter: */
+  demo_countdown--;
+  if (demo_countdown <= 0 || num_cities_alive == 0)
+    done = 1;
+}
 
-        /* Add a digit: */
+/* FIXME need to end game if no more questions left */
+void game_handle_answer(void)
+{
+  int i, num, lowest, lowest_y;
 
-	if (picked_comet != -1 && (frame % 5) == 0 && (rand() % 10) < 8)
-	{
-          tux_pressing = 1;
-	  
-          if (answer_digit < 3)
-	  {
-	    digits[0] = digits[1];
-	    digits[1] = digits[2];
+  doing_answer = 0;
 
-	    if (answer_digit == 0)
-	    {
-	      digits[2] = comets[picked_comet].answer / 100;
-	    }
-	    else if (answer_digit == 1)
-	    {
-	      digits[2] = (comets[picked_comet].answer % 100) / 10;
-	    }
-	    else if (answer_digit == 2)
-	    {
-	      digits[2] = (comets[picked_comet].answer % 10);
-	    }
-	    
-            answer_digit++;
-	  }
-	  else
-	  {
-            /* "Press Return" */
-
-	    doing_answer = 1;
-	    picked_comet = -1;
-	  }
-	}
-
-
-        /* Count down counter: */
+  num = (digits[0] * 100 +
+         digits[1] * 10 +
+         digits[2]);
+  /* negative answer support DSB */
+  if (neg_answer_picked)
+  {
+    num = -num;
+  }	
 	
-	demo_countdown--;
-	
-	if (demo_countdown <= 0 || num_cities_alive == 0)
-          done = 1;
-      }
-      /* end of demo mode code */
-      
-      /* Handle answer: */
-      
-      if (doing_answer)
-      {
-	doing_answer = 0;
+  /*  Pick the lowest comet which has the right answer: */
+  lowest_y = 0;
+  lowest = -1;
 
-	num = (digits[0] * 100 +
-	       digits[1] * 10 +
-	       digits[2]);
-	/* negative answer support DSB */
-        if (neg_answer_picked)
-	{
-	  num = -num;
-	}	
+  for (i = 0; i < MAX_COMETS; i++)
+  {
+    if (comets[i].alive &&
+        comets[i].expl < COMET_EXPL_END && 
+        comets[i].answer == num &&
+        comets[i].y > lowest_y)
+    {
+      lowest = i;
+      lowest_y = comets[i].y;
+    }
+  }
 	
-	/*  Pick the lowest comet which has the right answer: */
-	
-	lowest_y = 0;
-	lowest = -1;
-	
-	for (i = 0; i < MAX_COMETS; i++)
-	  {
-	    if (comets[i].alive &&
-	        comets[i].expl < COMET_EXPL_END && 
-	        comets[i].answer == num &&
-	        comets[i].y > lowest_y)
-	      {
-	        lowest = i;
-	        lowest_y = comets[i].y;
-	      }
-	  }
-	
-	
-	/* If there was an comet with this answer, destroy it! */
-	
-	if (lowest != -1)
-	  {
-            /* Destroy comet: */
-		  
-	    comets[lowest].expl = COMET_EXPL_START;
-	    
+  /* If there was an comet with this answer, destroy it! */
+  if (lowest != -1)  /* -1 means no comet had this answer */
+  {
+    MC_AnsweredCorrectly(&(comets[lowest].flashcard));
 
-	    /* Fire laser: */
+    /* Destroy comet: */
+    comets[lowest].expl = COMET_EXPL_START;
+    /* Fire laser: */
+    laser.alive = LASER_START;
+    laser.x1 = screen->w / 2;
+    laser.y1 = screen->h;
+    laser.x2 = comets[lowest].x;
+    laser.y2 = comets[lowest].y;
+    playsound(SND_LASER);
+    playsound(SND_SIZZLE);
 	    
-	    laser.alive = LASER_START;
-	    
-	    laser.x1 = screen->w / 2;
-	    laser.y1 = screen->h;
-	    
-	    laser.x2 = comets[lowest].x;
-	    laser.y2 = comets[lowest].y;
-	    
-	    playsound(SND_LASER);
-	    playsound(SND_SIZZLE);
-	    
-	    /* 50% of the time.. */
-	    
-	    if ((rand() % 10) < 5)
-	      {
-	        /* ... pick an animation to play: */
-	        
-	        if ((rand() % 10) < 5)
-	          tux_anim = IMG_TUX_YES1;
-	        else
-	          tux_anim = IMG_TUX_YAY1;
-	        
-	        tux_anim_frame = ANIM_FRAME_START;
-	      }
+    /* FIXME maybe should move this into game_handle_tux() */
+    /* 50% of the time.. */
+    if ((rand() % 10) < 5)
+    {
+      /* ... pick an animation to play: */
+      if ((rand() % 10) < 5)
+        tux_anim = IMG_TUX_YES1;
+      else
+        tux_anim = IMG_TUX_YAY1;
+      tux_anim_frame = ANIM_FRAME_START;
+    }
 
+    /* Increment score: */
 
-	    /* Increment score: */
-
-	    /* [ add = 25, sub = 50, mul = 75, div = 100 ] */
-	    /* [ the higher the better ] */
-
-	    add_score(((25 * (comets[lowest].oper + 1)) *
-		        (screen->h - comets[lowest].y + 1)) /
-		      screen->h);
-	  }
-	else
-	  {
-	    /* Didn't hit anything! */
+    /* [ add = 25, sub = 50, mul = 75, div = 100 ] */
+    /* [ the higher the better ] */
+    add_score(((25 * (comets[lowest].oper + 1)) *
+              (screen->h - comets[lowest].y + 1)) /
+	       screen->h);
+  }
+  else
+  {
+    /* Didn't hit anything! */
+    laser.alive = LASER_START;
+    laser.x1 = screen->w / 2;
+    laser.y1 = screen->h;
+    laser.x2 = laser.x1;
+    laser.y2 = 0;
+    playsound(SND_LASER);
+    playsound(SND_BUZZ);
 	    
-	    laser.alive = LASER_START;
-	    
-	    laser.x1 = screen->w / 2;
-	    laser.y1 = screen->h;
-	    
-	    laser.x2 = laser.x1;
-	    laser.y2 = 0;
-	    
-	    playsound(SND_LASER);
-	    playsound(SND_BUZZ);
-	    
-	    if ((rand() % 10) < 5)
-	      tux_img = IMG_TUX_DRAT;
-	    else
-	      tux_img = IMG_TUX_YIPE;
-	  }
+    if ((rand() % 10) < 5)
+      tux_img = IMG_TUX_DRAT;
+    else
+      tux_img = IMG_TUX_YIPE;
+  }
 	
-	
-	/* Clear digits: */
-	
-	digits[0] = 0;
-	digits[1] = 0;
-	digits[2] = 0;
-        neg_answer_picked = 0;
-      }
+  /* Clear digits: */
+  digits[0] = 0;
+  digits[1] = 0;
+  digits[2] = 0;
+  neg_answer_picked = 0;
+}
 
-      
-      /* Handle start-wait countdown: */
-      
-      if (level_start_wait > 0)
-	{
-	  level_start_wait--;
+void game_countdown(void)
+{
+  level_start_wait--;
+  if (level_start_wait > LEVEL_START_WAIT_START / 4)
+    tux_img = IMG_TUX_RELAX1;
+  else if (level_start_wait > 0)
+    tux_img = IMG_TUX_RELAX2;
+  else
+    tux_img = IMG_TUX_SIT;
 	  
-	  if (level_start_wait > LEVEL_START_WAIT_START / 4)
-	    tux_img = IMG_TUX_RELAX1;
-	  else if (level_start_wait > 0)
-	    tux_img = IMG_TUX_RELAX2;
-	  else
-	    tux_img = IMG_TUX_SIT;
-	  
-	  if (level_start_wait == LEVEL_START_WAIT_START / 4)
-	    {
-	      playsound(SND_ALARM);
-	    }
-	}
+  if (level_start_wait == LEVEL_START_WAIT_START / 4)
+  {
+    playsound(SND_ALARM);
+  }
+}
 
+void game_handle_tux(void)
+{
+  static int tux_same_counter;
+  /* If Tux pressed a button, pick a new (different!) stance: */
+  if (tux_pressing)
+  {
+
+    do
+    {
+      tux_img = IMG_TUX_CONSOLE1 + (rand() % 4);
+    }
+    while (tux_img == old_tux_img);
+
+    playsound(SND_CLICK);
+  }
+ 
+  /* If Tux is being animated, show the animation: */
+  if (tux_anim != -1)
+  {
+    tux_anim_frame--;
+    if (tux_anim_frame < 0)
+      tux_anim = -1;
+    else
+      tux_img = tux_anim + 1 - (tux_anim_frame / (ANIM_FRAME_START / 2));
+  }
+
+  /* Reset Tux to sitting if he's been doing nothing for a while: */
+  if (old_tux_img == tux_img)
+  {
+    tux_same_counter++;
+    if (tux_same_counter >= 20)
+    {
+      tux_img = IMG_TUX_SIT;
+    }
+  }
+  else
+    tux_same_counter = 0;
+}
+
+void game_handle_comets(void)
+{
+  int i;
+  /* Handle comets: */
+  num_comets_alive = 0;
       
-      /* If Tux pressed a button, pick a new (different!) stance: */
-	  
-      if (tux_pressing)
+  for (i = 0; i < MAX_COMETS; i++)
+  {
+    if (comets[i].alive)
+    {
+      num_comets_alive++;
+      /* update comet position */
+      comets[i].x = comets[i].x + 0; /* no lateral motion for now! */
+      comets[i].y = comets[i].y + (game_options->speed);
+	      
+      if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
+	  comets[i].expl < COMET_EXPL_END)
       {
-        do
+        printf("\nAbout to disable shields or destroy city!\n");
+        /* Disable shields or destroy city: */
+        MC_AnsweredIncorrectly(&(comets[i].flashcard));
+        if (cities[comets[i].city].shields)
 	{
-	  tux_img = IMG_TUX_CONSOLE1 + (rand() % 4);
+          printf("\nDisabling shields!\n");
+	  cities[comets[i].city].shields = 0;
+	  playsound(SND_SHIELDSDOWN);
 	}
-        while (tux_img == old_tux_img);
+        else
+        {
+          printf("\nDestroying city!\n");
+          cities[comets[i].city].expl = CITY_EXPL_START;
+          playsound(SND_EXPLOSION);
+        }
 
-	playsound(SND_CLICK);
+        tux_anim = IMG_TUX_FIST1;
+        tux_anim_frame = ANIM_FRAME_START;
+
+        /* Destroy comet: */
+        comets[i].expl = COMET_EXPL_START;
       }
-      
-      
-      /* If Tux is being animated, show the animation: */
 
-      if (tux_anim != -1)
+      /* Handle comet explosion animation: */
+      if (comets[i].expl >= COMET_EXPL_END)
       {
-	tux_anim_frame--;
-
-	if (tux_anim_frame < 0)
-          tux_anim = -1;
-	else
-	  tux_img = tux_anim + 1 - (tux_anim_frame / (ANIM_FRAME_START / 2));
+        comets[i].expl--;
+	if (comets[i].expl < COMET_EXPL_END)
+	  comets[i].alive = 0;
       }
+    }
+  }
 
-
-      /* Reset Tux to sitting if he's been doing nothing for a while: */
-
-      if (old_tux_img == tux_img)
+  /* add more comets if needed: */
+  if (level_start_wait == 0 &&
+      (frame % 20) == 0 &&
+      gameover == 0)
+  {
+    /* num_attackers is how many comets are left in wave */
+    if (num_attackers > 0)
+    {
+      if ((rand() % 2) == 0 || num_comets_alive == 0)
       {
-	tux_same_counter++;
-
-	if (tux_same_counter >= 20)
-	{
-          tux_img = IMG_TUX_SIT;
-	}
+        if (add_comet())
+        {
+	  num_attackers--;
+        }
       }
-      else
-	tux_same_counter = 0;
+    }
+    else
+    {
+      if (num_comets_alive == 0)
+      {
+        /* Time for the next wave! */
 
-
-      /* Handle comets: */
-     
-      num_comets_alive = 0;
-      
-      for (i = 0; i < MAX_COMETS; i++)
+	/* FIXME: End of level stuff goes here */
+        /* FIXME this belongs in game_handle_cities(), I think */
+        if (num_cities_alive > 0)
 	{
-	  if (comets[i].alive)
-	    {
-	      num_comets_alive++;
-
-	      comets[i].x = comets[i].x + 0;
-	      comets[i].y = comets[i].y + (game_options->speed * wave);
-	      
-	      if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
-	          comets[i].expl < COMET_EXPL_END)
-	      {
-		/* Disable shields or destroy city: */
-		      
-		if (cities[comets[i].city].shields)
-		{
-	          cities[comets[i].city].shields = 0;
-		  playsound(SND_SHIELDSDOWN);
-		}
-		else
-		{
-		  cities[comets[i].city].expl = CITY_EXPL_START;
-		  playsound(SND_EXPLOSION);
-		}
-
-		tux_anim = IMG_TUX_FIST1;
-		tux_anim_frame = ANIM_FRAME_START;
-
-
-		/* Destroy comet: */
-
-		comets[i].expl = COMET_EXPL_START;
-	      }
-
-
-	      /* Handle comet explosion animation: */
-
-	      if (comets[i].expl >= COMET_EXPL_END)
-	      {
-		comets[i].expl--;
-
-		if (comets[i].expl < COMET_EXPL_END)
-	          comets[i].alive = 0;
-	      }
-	    }
+          /* Go on to the next wave: */
+	  wave++;
+	  reset_level();
 	}
-
-
-      /* Handle laser: */
-
-      if (laser.alive > 0)
-	laser.alive--;
-      
-     
-      /* Comet time! */
-
-      if (level_start_wait == 0 && (frame % 20) == 0 &&
-	  gameover == 0)
-      {
-	if (num_attackers > 0)
-	{
-          /* More comets to add during this wave! */
-		
-	  if ((rand() % 2) == 0 || num_comets_alive == 0)
-	  {
-            add_comet();
-	    num_attackers--;
-	  }
-	}
 	else
 	{
-          if (num_comets_alive == 0)
-	  {
-            /* Time for the next wave! */
-
-	    /* FIXME: End of level stuff goes here */
-
-	    if (num_cities_alive > 0)
-	    {
-              /* Go on to the next wave: */
-		  
-              wave++;
-	      reset_level();
-	    }
-	    else
-	    {
-              /* No more cities!  Game over! */
-
-	      gameover = GAMEOVER_COUNTER_START;
-	    }
-	  }
+          /* No more cities!  Game over! */
+          gameover = GAMEOVER_COUNTER_START;
 	}
       }
+    }
+  }
+}
 
+void game_handle_cities(void)
+{
+  int i;
+  /* FIXME does the following variable do anything? */
+  num_cities_alive = 0;
 
-      /* Handle cities: */
-     
-      num_cities_alive = 0;
-
-      for (i = 0; i < NUM_CITIES; i++)
-	{
-	  if (cities[i].alive)
-	    {
-	      num_cities_alive++;
-
-
-	      /* Handle animated explosion: */
-
-	      if (cities[i].expl)
-		{
-		  cities[i].expl--;
-		  
-		  if (cities[i].expl == 0)
-		    cities[i].alive = 0;
-		}
-	    }
-	}
-
-
-      /* Handle game-over: */
-
-      if (gameover > 0)
+  for (i = 0; i < NUM_CITIES; i++)
+  {
+    if (cities[i].alive)
+    {
+      num_cities_alive++;
+      /* Handle animated explosion: */
+      if (cities[i].expl)
       {
-	gameover--;
-
-	if (gameover <= 0)
-          done = 1;
+        cities[i].expl--;
+	if (cities[i].expl == 0)
+	   cities[i].alive = 0;
       }
-      
-      
-      /* Clear screen: */
-     
-      if (bkgd == NULL)
-      {
-        dest.x = 0;
-        dest.y = 0;
-        dest.w = screen->w;
-        dest.h = ((screen->h) / 4) * 3;
+    }
+  }
+}
 
-        SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format,
-		     64,
-		     64 + ((wave * 32) % 192),
-		     128 - ((wave * 16) % 128)));
+void game_draw(void)
+{
+  int i,j, img;
+  SDL_Rect src, dest;
+  char str[64];
+  char* comet_str;
 
+  /* Clear screen: */
+  if (bkgd == NULL)
+  {
+    dest.x = 0;
+    dest.y = 0;
+    dest.w = screen->w;
+    dest.h = ((screen->h) / 4) * 3;
 
-        dest.x = 0;
-        dest.y = ((screen->h) / 4) * 3;
-        dest.w = screen->w;
-        dest.h = (screen->h) / 4;
+    SDL_FillRect(screen, &dest, 
+                 SDL_MapRGB(screen->format,
+                                    64,
+		                    64 + ((wave * 32) % 192),
+		                    128 - ((wave * 16) % 128)));
 
-        SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 64, 96, 64));
-      }
-      else
-        SDL_BlitSurface(bkgd, NULL, screen, NULL);
+    dest.x = 0;
+    dest.y = ((screen->h) / 4) * 3;
+    dest.w = screen->w;
+    dest.h = (screen->h) / 4;
 
+    SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 64, 96, 64));
+  }
+  else
+  {
+    SDL_BlitSurface(bkgd, NULL, screen, NULL);
+  }
 
-      /* Draw "Demo" */
+  /* Draw "Demo" */
+  if (game_options->demo_mode)
+  {
+    dest.x = (screen->w - images[IMG_DEMO]->w) / 2;
+    dest.y = (screen->h - images[IMG_DEMO]->h) / 2;
+    dest.w = images[IMG_DEMO]->w;
+    dest.h = images[IMG_DEMO]->h;
 
-      if (game_options->demo_mode)
-      {
-	dest.x = (screen->w - images[IMG_DEMO]->w) / 2;
-	dest.y = (screen->h - images[IMG_DEMO]->h) / 2;
-	dest.w = images[IMG_DEMO]->w;
-	dest.h = images[IMG_DEMO]->h;
+    SDL_BlitSurface(images[IMG_DEMO], NULL, screen, &dest);
+  }
 
-	SDL_BlitSurface(images[IMG_DEMO], NULL, screen, &dest);
-      }
+  /* Draw wave: */
+  dest.x = 0;
+  dest.y = 0;
+  dest.w = images[IMG_WAVE]->w;
+  dest.h = images[IMG_WAVE]->h;
 
+  SDL_BlitSurface(images[IMG_WAVE], NULL, screen, &dest);
 
-      /* Draw wave: */
+  sprintf(str, "%d", wave);
+  draw_numbers(str, images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10));
 
-      dest.x = 0;
-      dest.y = 0;
-      dest.w = images[IMG_WAVE]->w;
-      dest.h = images[IMG_WAVE]->h;
-
-      SDL_BlitSurface(images[IMG_WAVE], NULL, screen, &dest);
-
-      sprintf(str, "%d", wave);
-      draw_numbers(str, images[IMG_WAVE]->w + (images[IMG_NUMBERS]->w / 10));
-
-
-      /* Draw score: */
-
-      dest.x = (screen->w - ((images[IMG_NUMBERS]->w / 10) * 7) -
+  /* Draw score: */
+  dest.x = (screen->w - ((images[IMG_NUMBERS]->w / 10) * 7) -
 	        images[IMG_SCORE]->w);
-      dest.y = 0;
-      dest.w = images[IMG_SCORE]->w;
-      dest.h = images[IMG_SCORE]->h;
+  dest.y = 0;
+  dest.w = images[IMG_SCORE]->w;
+  dest.h = images[IMG_SCORE]->h;
 
-      SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
+  SDL_BlitSurface(images[IMG_SCORE], NULL, screen, &dest);
       
-      sprintf(str, "%.6d", score);
-      draw_numbers(str, screen->w - ((images[IMG_NUMBERS]->w / 10) * 6));
+  sprintf(str, "%.6d", score);
+  draw_numbers(str, screen->w - ((images[IMG_NUMBERS]->w / 10) * 6));
       
-      
-      /* Draw cities: */
-      
-      for (i = 0; i < NUM_CITIES; i++)
-	{
-	  /* Decide which image to display: */
-	 
-	  if (cities[i].alive)
-	    {
-	      if (cities[i].expl == 0)
-		img = IMG_CITY_BLUE;
-	      else
-		img = (IMG_CITY_BLUE_EXPL5 -
-	               (cities[i].expl / (CITY_EXPL_START / 5)));
-	    }
-	  else
-	    img = IMG_CITY_BLUE_DEAD;
+  /* Draw cities: */
+  for (i = 0; i < NUM_CITIES; i++)
+  {
+    /* Decide which image to display: */
+    if (cities[i].alive)
+    {
+      if (cities[i].expl == 0)
+        img = IMG_CITY_BLUE;
+      else
+        img = (IMG_CITY_BLUE_EXPL5 - (cities[i].expl / (CITY_EXPL_START / 5)));
+    }
+    else
+    {
+      img = IMG_CITY_BLUE_DEAD;
+    }
+    /* Change image to appropriate color: */
+    img = img + ((wave % MAX_CITY_COLORS) *
+	       (IMG_CITY_GREEN - IMG_CITY_BLUE));
 	  
-	  
-	  /* Change image to appropriate color: */
-	  
-	  img = img + ((wave % MAX_CITY_COLORS) *
-		       (IMG_CITY_GREEN - IMG_CITY_BLUE));
-	  /* img = img + ((i % MAX_CITY_COLORS) *
-	               (IMG_CITY_GREEN - IMG_CITY_BLUE)); */
-	  
-	  
-	  /* Draw it! */
-	  
-	  dest.x = cities[i].x - (images[img]->w / 2);
-	  dest.y = (screen->h) - (images[img]->h);
-	  dest.w = (images[img]->w);
-	  dest.h = (images[img]->h);
-	  
-	  SDL_BlitSurface(images[img], NULL,
-			  screen, &dest);
+    /* Draw it! */
+    dest.x = cities[i].x - (images[img]->w / 2);
+    dest.y = (screen->h) - (images[img]->h);
+    dest.w = (images[img]->w);
+    dest.h = (images[img]->h);
+  
+    SDL_BlitSurface(images[img], NULL, screen, &dest);
 
-
-	  /* Draw sheilds: */
-
-	  if (cities[i].shields)
-	  {
-            for (j = (frame % 3); j < images[IMG_SHIELDS]->h; j = j + 3)
-	    {
+    /* Draw sheilds: */
+    if (cities[i].shields)
+    {
+      for (j = (frame % 3); j < images[IMG_SHIELDS]->h; j = j + 3)
+      {
 	      src.x = 0;
 	      src.y = j;
 	      src.w = images[IMG_SHIELDS]->w;
@@ -726,170 +871,233 @@
 	      dest.h = src.h;
 
 	      SDL_BlitSurface(images[IMG_SHIELDS], &src, screen, &dest);
-	    }
-	  }
-	}
+      }
+    }
+  }
 
 
-      /* Draw comets: */
-      
-      for (i = 0; i < MAX_COMETS; i++)
-	{
-	  if (comets[i].alive)
-	    { 
-	      if (comets[i].expl < COMET_EXPL_END)
-	      {
-	        /* Decide which image to display: */
-	        img = IMG_COMET1 + ((frame + i) % 3);
-
-		/* Display the formula (flashing, in the bottom half
+  /* Draw comets: */
+ for (i = 0; i < MAX_COMETS; i++)
+ {
+   if (comets[i].alive)
+   { 
+     if (comets[i].expl < COMET_EXPL_END)
+     {
+       /* Decide which image to display: */
+       img = IMG_COMET1 + ((frame + i) % 3);
+       /* Display the formula (flashing, in the bottom half
 		   of the screen) */
-		if (comets[i].y < screen->h / 2 || frame % 8 < 6)
-		  comet_str = comets[i].formula;
-		else comet_str = NULL;
-	      }
-	      else
-	      {
-		img = comets[i].expl;
-		comet_str = comets[i].answer_str;
-	      }
+       if (comets[i].y < screen->h / 2 || frame % 8 < 6)
+       {
+         comet_str = comets[i].formula;
+       }
+       else 
+       {
+         comet_str = NULL;
+       }
+      }
+      else
+      {
+        img = comets[i].expl;
+        comet_str = comets[i].answer_str;
+      }
 	      
-	      /* Draw it! */
-	      dest.x = comets[i].x - (images[img]->w / 2);
-	      dest.y = comets[i].y - images[img]->h;
-	      dest.w = images[img]->w;
-	      dest.h = images[img]->h;
+      /* Draw it! */
+      dest.x = comets[i].x - (images[img]->w / 2);
+      dest.y = comets[i].y - images[img]->h;
+      dest.w = images[img]->w;
+      dest.h = images[img]->h;
 	      
-	      SDL_BlitSurface(images[img], NULL, screen, &dest);
-	      if (comet_str != NULL)
-		draw_nums(comet_str, comets[i].x, comets[i].y);
-	    }
-	}
+      SDL_BlitSurface(images[img], NULL, screen, &dest);
+      if (comet_str != NULL)
+      {
+        draw_nums(comet_str, comets[i].x, comets[i].y);
+      }
+    }
+  }
 
-
-      /* Draw laser: */
-
-      if (laser.alive)
-      {
-	draw_line(laser.x1, laser.y1, laser.x2, laser.y2,
+  /* Draw laser: */
+  if (laser.alive)
+  {
+    draw_line(laser.x1, laser.y1, laser.x2, laser.y2,
 		  255 / (LASER_START - laser.alive),
 		  192 / (LASER_START - laser.alive),
 		  64);
-      }
+  }
 
+  /* Draw numeric keypad: */
+  if (game_options->use_keypad)
+  {
+    /* pick image to draw: */
+    int keypad_image;
+    if (MC_AllowNegAnswer())
+    {
+      /* draw regular keypad */
+      keypad_image = IMG_KEYPAD;
+    }
+    else
+    {
+      /* draw keypad with with grayed-out '+' and '-' */
+      keypad_image = IMG_KEYPAD_NO_NEG;
+    }
 
-      /* Draw numeric keypad: */
-      if (game_options->use_keypad)
-      {
-        if (math_options->allow_neg_answer)
-        /* draw regular keypad */
-        { 
-          dest.x = (screen->w - images[IMG_KEYPAD]->w) / 2;
-          dest.y = (screen->h - images[IMG_KEYPAD]->h) / 2;
-          dest.w = images[IMG_KEYPAD]->w;
-          dest.h = images[IMG_KEYPAD]->h;
-          SDL_BlitSurface(images[IMG_KEYPAD], NULL, screen, &dest);
-        }
-        else
-        /* draw keypad with with grayed-out '+' and '-' */
-        { 
-          dest.x = (screen->w - images[IMG_KEYPAD_NO_NEG]->w) / 2;
-          dest.y = (screen->h - images[IMG_KEYPAD_NO_NEG]->h) / 2;
-          dest.w = images[IMG_KEYPAD_NO_NEG]->w;
-          dest.h = images[IMG_KEYPAD_NO_NEG]->h;
-          SDL_BlitSurface(images[IMG_KEYPAD_NO_NEG], NULL, screen, &dest);
-        } 
-      }
+    /* now draw it: */ 
+    dest.x = (screen->w - images[keypad_image]->w) / 2;
+    dest.y = (screen->h - images[keypad_image]->h) / 2;
+    dest.w = images[keypad_image]->w;
+    dest.h = images[keypad_image]->h;
+    SDL_BlitSurface(images[keypad_image], NULL, screen, &dest);
+  }
 
+  /* Draw console & tux: */
+  /* FIXME add counter for remaining questions */
+  draw_console_image(IMG_CONSOLE);
 
-      /* Draw console & tux: */
+  if (gameover > 0)
+  {
+    tux_img = IMG_TUX_FIST1 + ((frame / 2) % 2);
+  }
+  draw_console_image(tux_img);
 
-      draw_console_image(IMG_CONSOLE);
+  /* LED code moved into separate function by DSB */
+  draw_led_nums();
 
-      if (gameover > 0)
-      {
-	tux_img = IMG_TUX_FIST1 + ((frame / 2) % 2);
-      }
+  /* Draw "Game Over" */
+  if (gameover > 0)
+  {
+    dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
+    dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
+    dest.w = images[IMG_GAMEOVER]->w;
+    dest.h = images[IMG_GAMEOVER]->h;
+	
+    SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
+   }
+ 
+  /* Swap buffers: */
+  SDL_Flip(screen);
+}
 
-      draw_console_image(tux_img);
+int check_exit_conditions(void)
+{
+  if (SDL_quit_received)
+  {
+    return GAME_OVER_WINDOW_CLOSE;
+  }
 
-      /* LED code moved into separate function by DSB */
-      draw_led_nums();
+  if (escape_received)
+  {
+    return GAME_OVER_ESCAPE;
+  }
 
-      /* Draw "Game Over" */
+  /* determine if game lost (i.e. all cities blown up): */
+  {
+    int i;
+    int surviving_cities = 0;
 
-      if (gameover > 0)
+    for (i = 0; i < NUM_CITIES; i++)
+    {
+      if (cities[i].alive)
       {
-	dest.x = (screen->w - images[IMG_GAMEOVER]->w) / 2;
-	dest.y = (screen->h - images[IMG_GAMEOVER]->h) / 2;
-	dest.w = images[IMG_GAMEOVER]->w;
-	dest.h = images[IMG_GAMEOVER]->h;
-	
-        SDL_BlitSurface(images[IMG_GAMEOVER], NULL, screen, &dest);
+        surviving_cities++;
       }
-      
-      
-      /* Swap buffers: */
-      
-      SDL_Flip(screen);
+    }
 
+    if (!surviving_cities)
+    {
+      return GAME_OVER_LOST;
+    }
+  }
 
-      /* If we're in "PAUSE" mode, pause! */
+  /* determine if game won (i.e. all questions in mission answered correctly): */
+  if (MC_MissionAccomplished())
+  {
+    return GAME_OVER_WON;
+  }
 
-      if (paused)
-        {
-	  quit = pause_game();
-	  paused = 0;
-        }
+  /* Could have situation where mathcards doesn't have more questions */
+  /* even though not all questions answered correctly:                */
+  if (MC_GameOver())
+  {
+    return GAME_OVER_OTHER;
+  }
 
-      
-      /* Keep playing music: */
-      
-#ifndef NOSOUND
-      if (game_options->use_sound)
-	{
-	  if (!Mix_PlayingMusic())
-	    Mix_PlayMusic(musics[MUS_GAME + (rand() % 3)], 0);
-	}
-#endif
-      
-      
-      /* Pause (keep frame-rate event) */
-      
-      now_time = SDL_GetTicks();
-      if (now_time < last_time + FPS)
-	SDL_Delay(last_time + FPS - now_time);
-    }
-  while (!done && !quit);
+  /* Need to get out if no comets alive and MathCards out of unanswered questions, */
+  /* even though MathCards thinks there are still questions "in play".  */
+  /* This SHOULD NOT HAPPEN and means we have a bug somewhere. */
 
-  
-  /* Free background: */
+  {
+//     int i;
+//     int living_comets = 0;
+// 
+//     for (i = 0; i < MAX_COMETS; i++)
+//     {
+//       living_comets += comets[i].alive;
+//     }
 
-  if (bkgd != NULL)
-    SDL_FreeSurface(bkgd);
+    if (MC_NoQuestionsLeft() && !num_comets_alive)
+    {
+      #ifdef TUXMATH_DEBUG
+      printf("\nNoQuestionsLeft() = %d", MC_NoQuestionsLeft());
+      printf("\nnum_comets_alive = %d", num_comets_alive);
+      #endif 
+      return GAME_OVER_ERROR;
+    }
+  }
 
+  /* if we made it to here, the game goes on! */
+  return GAME_IN_PROGRESS;
+}
 
-
-  /* Stop music: */
-#ifndef NOSOUND
-  if (game_options->use_sound)
+void print_exit_conditions(void)
+{
+  printf("\ngame_status:\t");
+  switch (game_status)
   {
-    if (Mix_PlayingMusic())
+    case GAME_IN_PROGRESS:
     {
-      Mix_HaltMusic();
+      printf("GAME_IN_PROGRESS\n");
+      break;
     }
+
+    case GAME_OVER_WON:
+    {
+      printf("GAME_OVER_WON\n");
+      break;
+    }
+    case GAME_OVER_LOST:
+    {
+      printf("GAME_OVER_LOST\n");
+      break;
+    }
+    case GAME_OVER_OTHER:
+    {
+      printf("GAME_OVER_OTHER\n");
+      break;
+    }
+    case GAME_OVER_ESCAPE:
+    {
+      printf("GAME_OVER_ESCAPE\n");
+      break;
+    }
+    case GAME_OVER_WINDOW_CLOSE:
+    {
+      printf("GAME_OVER_WINDOW_CLOSE\n");
+      break;
+    }
+    case GAME_OVER_ERROR:
+    {
+      printf("GAME_OVER_ERROR\n");
+      break;
+    }
+    default:
+    {
+      printf("Unrecognized value\n");
+      break;
+    }
   }
-#endif
-  
-  
-  /* Return the chosen command: */
-  
-  return quit;
 }
 
-
 /* Reset stuff for the next level! */
-
 void reset_level(void)
 {
   char fname[1024];
@@ -899,9 +1107,11 @@
   /* Clear all comets: */
   
   for (i = 0; i < MAX_COMETS; i++)
+  {
     comets[i].alive = 0;
-  
-  
+  }
+  num_comets_alive = 0;
+
   /* Clear LED digits: */
   
   digits[0] = 0;
@@ -948,14 +1158,30 @@
 
 
   /* Set number of attackers for this wave: */
+  /* num_attackers = 2 * wave; */ 
 
-  num_attackers = 2 * wave;  /* FIXME: Is this good? */
+  /* increase number of comets and speed if desired */
+  if (game_options->allow_speedup)
+  {
+    num_attackers = wave * game_options->extra_comets;
+    game_options->speed = (game_options->speed) * game_options->speedup_factor;
+
+    if (game_options->speed > game_options->max_speed)
+    {
+      game_options->speed = game_options->max_speed;
+    } 
+  }
+  else
+  {
+    num_attackers = 2;
+  }
 }
 
 
-/* Add an comet to the game (if there's room): */
 
-void add_comet(void)
+/* Add a comet to the game (if there's room): */
+
+int add_comet(void)
 {
   static int prev_city = -1;
   int i, found;
@@ -966,129 +1192,66 @@
   found = -1;
   
   for (i = 0; i < MAX_COMETS && found == -1; i++)
+  {
+    if (comets[i].alive == 0)
     {
-      if (comets[i].alive == 0)
-	{
-	  found = i;
-	}
+      found = i;
     }
+  }
   
   
   if (found != -1)
+  {
+    /* Get math question for new comet: */
+    if (!MC_NextQuestion(&(comets[found].flashcard)))
+    /* no more questions - cannot create comet.  */ 
     {
-      comets[found].alive = 1;
+      return 0;
+    }
 
+    /* FIXME streamline comet struct - for now assign to old variables */
+    /* FIXME also need to handle question formats */
+    comets[found].eq1 = comets[found].flashcard.num1;
+    comets[found].oper = comets[found].flashcard.operation;
+    comets[found].eq2 = comets[found].flashcard.num2;
+    comets[found].answer = comets[found].flashcard.num3;
 
-      /* Pick a city to attack that was not attacked last time */
-      /* (so formulas are less likely to overlap). */
-      do 
-	i = rand() % NUM_CITIES;
-      while (i == prev_city);
-      prev_city = i;
-     
+    comets[found].alive = 1;
+    num_comets_alive++;      
 
-      /* Set in to attack that city: */
-      
-      comets[found].city = i; 
+    /* Pick a city to attack that was not attacked last time */
+    /* (so formulas are less likely to overlap). */
+    do 
+    {
+      i = rand() % NUM_CITIES;
+    }
+    while (i == prev_city);
 
-
-      /* Start at the top, above the city in question: */
-      
-      comets[found].x = cities[i].x;
-      comets[found].y = 0;
-
-
-      /* Pick an operation (+, -, *, /): */
-      /* FIXME now that negative answers a possibility, */
-      /* should test absolute value against max_answer  */
-      do
-      { 
-        comets[found].oper = (rand() % NUM_OPERS);
-      }
-      while (opers[comets[found].oper] == 0);
+    prev_city = i;
      
-      
-      if (comets[found].oper == OPER_ADD)
-	{
-	  /* Simple addition: */
+    /* Set in to attack that city: */
+    comets[found].city = i; 
+    /* Start at the top, above the city in question: */
+    comets[found].x = cities[i].x;
+    comets[found].y = 0;
 
-	  do
-	  {
-	    comets[found].eq1 = pick_operand(0);
-	    comets[found].eq2 = pick_operand(0);
-	    comets[found].answer = comets[found].eq1 + comets[found].eq2;
-	  }
-	  while (comets[found].answer > math_options->max_answer);
-	}
+    snprintf(comets[found].formula, FORMULA_LEN,"%d%c%d",
+	     comets[found].eq1,
+	     operchars[comets[found].oper],
+	     comets[found].eq2);
+    snprintf(comets[found].answer_str, ANSWER_LEN, "%d",
+	     comets[found].answer);
 
-      else if (comets[found].oper == OPER_SUB)
-	{
-	  /* Subtraction: */
-
-	  do
-	  {
-	    comets[found].eq1 = pick_operand(0);
-	    comets[found].eq2 = pick_operand(0);
-	    /* try again until answer not negative  */
-	    /* unless neg_answer_allowed DSB */
-	    if (!math_options->allow_neg_answer)
-            {
-	      do
-	      {
-	        comets[found].eq2 = pick_operand(0);
-	      }
-	      while (comets[found].eq2 > comets[found].eq1);
-            }
-	    comets[found].answer = comets[found].eq1 - comets[found].eq2;
-	  }
-	  while (comets[found].answer > math_options->max_answer);
-	}
-
-      else if (comets[found].oper == OPER_MULT)
-	{
-	  /* Multiplication: */
-	  do
-          {
-            comets[found].eq1 = pick_operand(1);
-	    comets[found].eq2 = pick_operand(0);
-	    comets[found].answer = comets[found].eq1 * comets[found].eq2;
-          }
-	  while (comets[found].answer > math_options->max_answer);
-	}
-
-      else if (comets[found].oper == OPER_DIV)
-	{
-	  /* Division: */
-
-	  do
-	  {
-	    /* (Don't divide by zero) */
-		
-	    comets[found].eq2 = pick_operand(1);
-
-
-	    /* (Make sure answer will be a whole (int) number) */
-	  
-	    comets[found].eq1 = pick_operand(0) * comets[found].eq2;
-
-	  
-	    comets[found].answer = comets[found].eq1 / comets[found].eq2;
-	  }
-	  while (comets[found].answer > math_options->max_answer ||
-		 !in_range(comets[found].eq1 / 2));
-	}
-      snprintf(comets[found].formula, FORMULA_LEN,"%d%c%d",
-	       comets[found].eq1,
-	       operchars[comets[found].oper],
-	       comets[found].eq2);
-      snprintf(comets[found].answer_str, ANSWER_LEN, "%d",
-	       comets[found].answer);
-    }
+    /* comet slot found and question found so return successfully: */
+    return 1;
+  }
+  /* free comet slot not found - no comet added: */
+  return 0;
 }
 
 
 /* Draw numbers/symbols over the attacker: */
-
+/* This draw the numbers related to the comets */
 void draw_nums(char * str, int x, int y)
 {
   int i, j, cur_x, c;
@@ -1213,12 +1376,14 @@
 
 int pause_game(void)
 {
-  int done, quit;
+  /* NOTE - done and quit changed to pause_done and pause_quit */
+  /* due to potentially confusing name collision */
+  int pause_done, pause_quit;
   SDL_Event event;
   SDL_Rect dest;
  
-  done = 0;
-  quit = 0;
+  pause_done = 0;
+  pause_quit = 0;
 
   dest.x = (screen->w - images[IMG_PAUSED]->w) / 2;
   dest.y = (screen->h - images[IMG_PAUSED]->h) / 2;
@@ -1240,14 +1405,18 @@
     while (SDL_PollEvent(&event))
     {
       if (event.type == SDL_KEYDOWN)
-	done = 1;
+	pause_done = 1;
       else if (event.type == SDL_QUIT)
-	quit = 1;
+      {
+        SDL_quit_received = 1;
+        /* FIXME quit is being superseded */
+ 	pause_quit = 1;
+      }
     }
 
     SDL_Delay(100);
   }
-  while (!done && !quit);
+  while (!pause_done && !pause_quit);
 
 
 #ifndef NOSOUND
@@ -1255,7 +1424,7 @@
     Mix_ResumeMusic();
 #endif
 
-  return (quit);
+  return (pause_quit);
 }  
 
 
@@ -1401,7 +1570,7 @@
 
   /* begin drawing so as to center display depending on whether minus */
   /* sign needed (4 digit slots) or not (3 digit slots) DSB */
-  if (math_options->allow_neg_answer)
+  if (MC_AllowNegAnswer())
     dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 4) / 2);
   else
     dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 3) / 2);
@@ -1411,7 +1580,7 @@
   { 
     if (-1 == i)
     {
-      if (math_options->allow_neg_answer)
+      if (MC_AllowNegAnswer())
       {
         if (neg_answer_picked)
           src.x =  (images[IMG_LED_NEG_SIGN]->w) / 2;
@@ -1451,10 +1620,10 @@
 }
 
 /* Translates mouse events into keyboard events when on-screen keypad used */
-void game_mouse_event(SDL_Event the_event)
+void game_mouse_event(SDL_Event event)
 {
   int keypad_w, keypad_h, x, y, row, column;
-  SDLKey keypad_key = SDLK_UNKNOWN;
+  SDLKey key = SDLK_UNKNOWN;
 
   keypad_w = 0;
   keypad_h = 0;
@@ -1469,7 +1638,7 @@
   /* make sure keypad image is valid and has non-zero dimensions: */
   /* FIXME maybe this checking should be done once at the start */
   /* of game() rather than with every mouse click */
-  if (math_options->allow_neg_answer)
+  if (MC_AllowNegAnswer())
   {
     if (!images[IMG_KEYPAD])
       return;
@@ -1497,13 +1666,13 @@
   }
 
   /* only proceed if click falls within keypad: */
-  if (!((the_event.button.x >=
+  if (!((event.button.x >=
         (screen->w / 2) - (keypad_w / 2) &&
-        the_event.button.x <=
+        event.button.x <=
         (screen->w / 2) + (keypad_w / 2) &&
-        the_event.button.y >= 
+        event.button.y >= 
         (screen->h / 2) - (keypad_h / 2) &&
-        the_event.button.y <=
+        event.button.y <=
         (screen->h / 2) + (keypad_h / 2))))
   /* click outside of keypad - do nothing */
   {
@@ -1512,8 +1681,8 @@
   
   else /* click was within keypad */ 
   {
-    x = (the_event.button.x - ((screen->w / 2) - (keypad_w / 2)));
-    y = (the_event.button.y - ((screen->h / 2) - (keypad_h / 2)));
+    x = (event.button.x - ((screen->w / 2) - (keypad_w / 2)));
+    y = (event.button.y - ((screen->h / 2) - (keypad_h / 2)));
  
   /* Now determine what onscreen key was pressed */
   /*                                             */
@@ -1559,69 +1728,71 @@
     if (0 == row)
     {
       if (0 == column)
-        keypad_key = SDLK_7;
+        key = SDLK_7;
       if (1 == column)
-        keypad_key = SDLK_8;
+        key = SDLK_8;
       if (2 == column)
-        keypad_key = SDLK_9;
+        key = SDLK_9;
       if (3 == column)
-        keypad_key = SDLK_MINUS;
+        key = SDLK_MINUS;
     } 
     if (1 == row)
     {
       if (0 == column)
-        keypad_key = SDLK_4;
+        key = SDLK_4;
       if (1 == column)
-        keypad_key = SDLK_5;
+        key = SDLK_5;
       if (2 == column)
-        keypad_key = SDLK_6;
+        key = SDLK_6;
       if (3 == column)
-        keypad_key = SDLK_PLUS;
+        key = SDLK_PLUS;
     }     
     if (2 == row)
     {
       if (0 == column)
-        keypad_key = SDLK_1;
+        key = SDLK_1;
       if (1 == column)
-        keypad_key = SDLK_2;
+        key = SDLK_2;
       if (2 == column)
-        keypad_key = SDLK_3;
+        key = SDLK_3;
       if (3 == column)
-        keypad_key = SDLK_PLUS;
+        key = SDLK_PLUS;
     } 
     if (3 == row)
     {
       if (0 == column)
-        keypad_key = SDLK_0;
+        key = SDLK_0;
       if (1 == column)
-        keypad_key = SDLK_RETURN;
+        key = SDLK_RETURN;
       if (2 == column)
-        keypad_key = SDLK_RETURN;
+        key = SDLK_RETURN;
       if (3 == column)
-        keypad_key = SDLK_RETURN;
+        key = SDLK_RETURN;
     }     
 
-    if (keypad_key == SDLK_UNKNOWN)
+    if (key == SDLK_UNKNOWN)
     {
       return;
     }
 
     /* now can proceed as if keyboard was used */
-    game_key_event(keypad_key);
+    game_key_event(key);
   }
 }
 
 /* called by either key presses or mouse clicks on */
 /* on-screen keypad */
-void game_key_event(SDLKey pressed_key)
+void game_key_event(SDLKey key)
 {
-  if (pressed_key == SDLK_ESCAPE)
+  if (key == SDLK_ESCAPE)
   {
     /* Escape key - quit! */
-   done = 1;
+    escape_received = 1;
+    /* FIXME done will be superseded */
+    done = 1;
   }
-  else if (pressed_key == SDLK_TAB
-        || pressed_key == SDLK_p)
+  else if (key == SDLK_TAB
+        || key == SDLK_p)
   {
   /* [TAB] or [P]: Pause! */
     paused = 1;
@@ -1631,43 +1802,43 @@
   {
     /* Eat other keys until level start wait has passed,
     or if game is in demo mode: */
-    pressed_key = SDLK_UNKNOWN;
+    key = SDLK_UNKNOWN;
   }
 	      
-  if (pressed_key >= SDLK_0 && pressed_key <= SDLK_9)
+  if (key >= SDLK_0 && key <= SDLK_9)
   {
     /* [0]-[9]: Add a new digit: */
     digits[0] = digits[1];
     digits[1] = digits[2];
-    digits[2] = pressed_key - SDLK_0;
+    digits[2] = key - SDLK_0;
     tux_pressing = 1;
   }
-  else if (pressed_key >= SDLK_KP0 && pressed_key <= SDLK_KP9)
+  else if (key >= SDLK_KP0 && key <= SDLK_KP9)
   {
     /* Keypad [0]-[9]: Add a new digit: */
     digits[0] = digits[1];
     digits[1] = digits[2];
-    digits[2] = pressed_key - SDLK_KP0;
+    digits[2] = key - SDLK_KP0;
     tux_pressing = 1;
   }
   /* support for negative answer input DSB */
-  else if ((pressed_key == SDLK_MINUS || pressed_key == SDLK_KP_MINUS)
-        && math_options->allow_neg_answer)  /* do nothing unless neg answers allowed */
+  else if ((key == SDLK_MINUS || key == SDLK_KP_MINUS)
+        && MC_AllowNegAnswer())  /* do nothing unless neg answers allowed */
   {
     /* allow player to make answer negative: */
     neg_answer_picked = 1;
     tux_pressing = 1;
   }
-  else if ((pressed_key == SDLK_PLUS || pressed_key == SDLK_KP_PLUS)
-         && math_options->allow_neg_answer)  /* do nothing unless neg answers allowed */
+  else if ((key == SDLK_PLUS || key == SDLK_KP_PLUS)
+         && MC_AllowNegAnswer())  /* do nothing unless neg answers allowed */
   {
     /* allow player to make answer positive: */
     neg_answer_picked = 0;
     tux_pressing = 1;
   }
-  else if (pressed_key == SDLK_BACKSPACE ||
-           pressed_key == SDLK_CLEAR ||
-	   pressed_key == SDLK_DELETE)
+  else if (key == SDLK_BACKSPACE ||
+           key == SDLK_CLEAR ||
+	   key == SDLK_DELETE)
   {
     /* [BKSP]: Clear digits! */
     digits[0] = 0;
@@ -1675,9 +1846,9 @@
     digits[2] = 0;
     tux_pressing = 1;
   }
-  else if (pressed_key == SDLK_RETURN ||
-           pressed_key == SDLK_KP_ENTER ||
-	   pressed_key == SDLK_SPACE)
+  else if (key == SDLK_RETURN ||
+           key == SDLK_KP_ENTER ||
+	   key == SDLK_SPACE)
   {
     /* [ENTER]: Accept digits! */
     doing_answer = 1;
@@ -1728,3 +1899,21 @@
   return ok;
 }
 
+void reset_comets(void)
+{
+  int i =0;
+  for (i = 0; i < MAX_COMETS; i++)
+  {
+    comets[i].alive = 0;
+    comets[i].expl = 0;
+    comets[i].city = 0;
+    comets[i].x = 0;
+    comets[i].y = 0;
+    comets[i].eq1 = 0;
+    comets[i].oper = 0;
+    comets[i].eq2 = 0;
+    comets[i].formula[FORMULA_LEN] = "";
+    comets[i].answer = 0;
+    comets[i].answer_str[ANSWER_LEN] = "";
+  }
+}

Modified: tuxmath/trunk/src/game.h
===================================================================
--- tuxmath/trunk/src/game.h	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/game.h	2006-05-16 17:05:20 UTC (rev 10)
@@ -58,8 +58,25 @@
   NUM_Q_RANGES
 };
 
+typedef struct range_type {
+  int min;
+  int max;
+} range_type;
+
+extern range_type ranges[NUM_Q_RANGES];
+
+enum {
+  GAME_IN_PROGRESS,
+  GAME_OVER_WON,
+  GAME_OVER_LOST,
+  GAME_OVER_OTHER,
+  GAME_OVER_ESCAPE,
+  GAME_OVER_WINDOW_CLOSE,
+  GAME_OVER_ERROR
+};
+
 int game(void);
-
+/* draw_nums() is used in options.c so need extern linkage */
 void draw_nums(char* str, int x, int y);
 
 #endif

Added: tuxmath/trunk/src/mathcards.c
===================================================================
--- tuxmath/trunk/src/mathcards.c	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/mathcards.c	2006-05-16 17:05:20 UTC (rev 10)
@@ -0,0 +1,1952 @@
+/*
+*  C Implementation: mathcards.c
+*
+* 	Description: implementation of backend for a flashcard-type math game. 
+        Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
+        (aka tuxmath).  (If tuxmath were a C++ program, this would be a C++ class).
+        MathCards could be used as the basis for similar games using a different interface.
+	 
+*
+*
+* Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2005
+*
+* Copyright: See COPYING file that comes with this distribution.  (Briefly, GNU GPL).
+*
+*/
+
+#include "mathcards.h"
+
+/* "Globals" for mathcards.c: */
+MC_Options* math_opts = 0;
+MC_MathQuestion* question_list = 0;
+MC_MathQuestion* wrong_quests = 0;
+MC_MathQuestion* next_wrong_quest = 0;
+int initialized = 0;
+int quest_list_length = 0;
+int answered_correctly = 0;
+int answered_wrong = 0;
+int questions_pending = 0;
+int unanswered = 0;
+int starting_length = 0;
+
+/* "private" function prototypes:                        */
+/*                                                       */
+/* these are for internal use by MathCards only - like   */
+/* the private functions of a C++ class. Declared static */
+/* to give file scope rather than extern scope.          */
+
+static MC_MathQuestion* generate_list(void);
+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_FlashCard*    create_card_from_node(MC_MathQuestion* node);
+static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
+static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
+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);
+static MC_MathQuestion* delete_list(MC_MathQuestion* list);
+static int list_length(MC_MathQuestion* list);
+
+static MC_MathQuestion* randomize_list(MC_MathQuestion* list);
+static MC_MathQuestion* pick_random(int length, MC_MathQuestion* list);
+static int compare_node(MC_MathQuestion* first, MC_MathQuestion* other);
+static int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr);
+static int int_to_bool(int i);
+static int sane_value(int i);
+static int abs_value(int i);
+
+static void print_math_options(void);
+static void print_list(MC_MathQuestion* list);
+static void print_node(MC_MathQuestion* ptr);
+static void print_card(MC_FlashCard card);
+static void print_counters(void);
+
+/* FIXME: Program should load options from disk */
+/*  MC_Initialize() sets up the struct containing all of  */
+/*  settings regarding math questions.  It should be      */
+/*  called before any other function.  Many of the other  */
+/*  functions will not work properly if MC_Initialize()   */
+/*  has not been called. It only needs to be called once, */  
+/*  i.e when the program is starting, not at the beginning*/
+/*  of each math game for the player. Returns 1 if        */
+/*  successful, 0 otherwise.                              */
+int MC_Initialize(void)
+{
+  #ifdef MC_DEBUG
+  printf("\nEntering MC_Initialize()");
+  #endif
+
+  /* check flag to see if we did this already */
+  if (initialized)
+  {
+
+    #ifdef MC_DEBUG
+    printf("\nAlready initialized");  
+    print_math_options(); 
+    printf("\nLeaving MC_Initialize()\n");
+    #endif
+
+    return 1;
+  }
+  math_opts = malloc(sizeof(MC_Options));
+  /* bail out if no struct */
+  if (!math_opts)
+  {
+
+    #ifdef MC_DEBUG
+    printf("\nError: math_opts null or invalid");
+    printf("\nLeaving MC_Initialize()\n");
+    #endif
+
+    fprintf(stderr, "\nUnable to initialize math_options");
+    return 0;
+  } 
+
+  /* set general math options */
+  math_opts->allow_neg_answer = DEFAULT_ALLOW_NEG_ANSWER;
+  math_opts->max_answer = DEFAULT_MAX_ANSWER;
+  math_opts->max_questions = DEFAULT_MAX_QUESTIONS;
+  math_opts->recycle_corrects = DEFAULT_RECYCLE_CORRECTS;
+  math_opts->recycle_wrongs = DEFAULT_RECYCLE_WRONGS;
+  math_opts->copies_recycled_wrongs = DEFAULT_COPIES_RECYCLED_WRONGS;
+  math_opts->format_answer_last = DEFAULT_FORMAT_ANSWER_LAST;
+  math_opts->format_answer_first = DEFAULT_FORMAT_ANSWER_FIRST;
+  math_opts->format_answer_middle = DEFAULT_FORMAT_ANSWER_MIDDLE;
+  math_opts->question_copies = DEFAULT_QUESTION_COPIES;
+  math_opts->randomize = DEFAULT_RANDOMIZE;
+  /* set addition options */
+  math_opts->addition_allowed = DEFAULT_ADDITION_ALLOWED;
+  math_opts->min_augend = DEFAULT_MIN_AUGEND;
+  math_opts->max_augend = DEFAULT_MAX_AUGEND;
+  math_opts->min_addend = DEFAULT_MIN_ADDEND;
+  math_opts->max_addend = DEFAULT_MAX_ADDEND;
+  /* set subtraction options */
+  math_opts->subtraction_allowed = DEFAULT_SUBTRACTION_ALLOWED;
+  math_opts->min_minuend = DEFAULT_MIN_MINUEND;
+  math_opts->max_minuend = DEFAULT_MAX_MINUEND;
+  math_opts->min_subtrahend = DEFAULT_MIN_SUBTRAHEND;
+  math_opts->max_subtrahend = DEFAULT_MAX_SUBTRAHEND;
+  /* set multiplication options */
+  math_opts->multiplication_allowed = DEFAULT_MULTIPLICATION_ALLOWED;
+  math_opts->min_multiplier = DEFAULT_MIN_MULTIPLIER;
+  math_opts->max_multiplier = DEFAULT_MAX_MULTIPLIER;
+  math_opts->min_multiplicand = DEFAULT_MIN_MULTIPLICAND;
+  math_opts->max_multiplicand = DEFAULT_MAX_MULTIPLICAND;
+  /* set division options */
+  math_opts->division_allowed = DEFAULT_DIVISION_ALLOWED;
+  math_opts->min_divisor = DEFAULT_MIN_DIVISOR;
+  math_opts->max_divisor = DEFAULT_MAX_DIVISOR;
+  math_opts->min_quotient = DEFAULT_MIN_QUOTIENT;
+  math_opts->max_quotient = DEFAULT_MAX_QUOTIENT;
+
+  /* if no negatives to be used, reset any negatives to 0 */
+  if (!math_opts->allow_neg_answer)
+  {
+    if (math_opts->min_augend < 0)
+      math_opts->min_augend = 0;
+    if (math_opts->max_augend < 0)
+      math_opts->max_augend = 0;
+    if (math_opts->min_addend < 0)
+      math_opts->min_addend = 0;
+    if (math_opts->max_addend < 0)
+      math_opts->max_addend = 0;
+
+    if (math_opts->min_minuend < 0)
+      math_opts->min_minuend = 0;
+    if (math_opts->max_minuend < 0)
+      math_opts->max_minuend = 0;
+    if (math_opts->min_subtrahend < 0)
+      math_opts->min_subtrahend = 0;
+    if (math_opts->max_subtrahend < 0)
+      math_opts->max_subtrahend = 0;
+
+    if (math_opts->min_multiplier < 0)
+      math_opts->min_multiplier = 0;
+    if (math_opts->max_multiplier < 0)
+      math_opts->max_multiplier = 0;
+    if (math_opts->min_multiplicand < 0)
+      math_opts->min_multiplicand = 0;
+    if (math_opts->max_multiplicand < 0)
+      math_opts->max_multiplicand = 0;
+
+    if (math_opts->min_divisor < 0)
+      math_opts->min_divisor = 0;
+    if (math_opts->max_divisor < 0)
+      math_opts->max_divisor = 0;
+    if (math_opts->min_quotient < 0)
+      math_opts->min_quotient = 0;
+    if (math_opts->max_quotient < 0)
+      math_opts->max_quotient = 0;
+  }
+  initialized = 1;
+
+  #ifdef MC_DEBUG
+  print_math_options(); 
+  printf("\nLeaving MC_Initialize()\n");
+  #endif 
+
+  return 1;
+}
+
+
+/*  MC_StartGame() generates the list of math questions   */
+/*  based on existing settings. It should be called at    */
+/*  the beginning of each math game for the player.       */
+/*  Returns 1 if resultant list contains 1 or more        */
+/*  questions, 0 if list empty or not generated           */
+/*  successfully.                                         */
+int MC_StartGame(void)
+{
+  #ifdef MC_DEBUG
+  printf("\nEntering MC_StartGame()");
+  #endif
+
+  /* if math_opts not set up yet, initialize it: */
+  if (!initialized)
+  {
+
+    #ifdef MC_DEBUG
+    printf("\nNot initialized - calling MC_Initialize()");
+    #endif  
+
+    MC_Initialize();
+  }
+
+  if (!math_opts)  
+  {
+    #ifdef MC_DEBUG
+    printf("\nCould not initialize - bailing out");
+    printf("\nLeavinging MC_StartGame()\n");
+    #endif
+
+    return 0;
+  }
+  /* we know math_opts exists if we make it to here */
+  
+  /* clear out old lists if starting another game: (if not done already) */
+  delete_list(question_list);
+  delete_list(wrong_quests);
+
+  /* set up new list with pointer to top: */
+  question_list = generate_list();
+
+  next_wrong_quest = 0;
+  /* initialize counters for new game: */
+  quest_list_length = list_length(question_list);
+  /* Note: the distinction between quest_list_length and  */
+  /* unanswered is that the latter includes questions     */
+  /* that are currently "in play" by the user interface - */
+  /* it is only decremented when an answer to the question*/
+  /* is received.                                         */
+  unanswered = starting_length = quest_list_length;
+  answered_correctly = 0;
+  answered_wrong = 0;
+  questions_pending = 0;
+
+  #ifdef MC_DEBUG
+  print_counters();
+  #endif
+
+  /* make sure list now exists and has non-zero length: */
+  if (question_list && quest_list_length)
+  {
+    #ifdef MC_DEBUG
+    printf("\nGame set up successfully");
+    printf("\nLeaving MC_StartGame()\n");
+    #endif
+
+    return 1;
+  }
+  else
+  {
+    #ifdef MC_DEBUG
+    printf("\nGame NOT set up successfully - no valid list");
+    printf("\nLeaving MC_StartGame()\n");
+    #endif
+
+    return 0;
+  }
+}
+
+/*  MC_StartGameUsingWrongs() is like MC_StartGame(),     */
+/*  but uses the incorrectly answered questions from the  */
+/*  previous game for the question list as a review form  */
+/*  of learning. If there were no wrong answers (or no    */
+/*  previous game), it behaves just like MC_StartGame().  */
+/*  FIXME wonder if it should return a different value if */
+/*  the list is created from settings because there is no */
+/*  valid wrong question list?                            */
+int MC_StartGameUsingWrongs(void)
+{
+  #ifdef MC_DEBUG
+  printf("\nEntering MC_StartGameUsingWrongs()");
+  #endif
+
+  /* Note: if not initialized, control will pass to       */
+  /* MC_StartGame() via else clause so don't need to test */
+  /* for initialization here                              */
+  if (wrong_quests &&
+      list_length(wrong_quests))
+  {
+    #ifdef MC_DEBUG
+    printf("\nNon-zero length wrong_quests list found, will");
+    printf("\nuse for new game list:");
+    #endif
+
+    /* initialize lists for new game: */
+    delete_list(question_list);
+    question_list = randomize_list(wrong_quests);
+    wrong_quests = 0;
+    next_wrong_quest = 0; 
+   /* initialize counters for new game: */
+    quest_list_length = list_length(question_list);
+    unanswered = starting_length = quest_list_length;
+    answered_correctly = 0;
+    answered_wrong = 0;
+    questions_pending = 0;
+
+    #ifdef MC_DEBUG
+    print_counters();
+    print_list(question_list);
+    printf("\nLeaving MC_StartGameUsingWrongs()\n");
+    #endif
+
+    return 1;
+  }
+  else /* if no wrong_quests list, go to MC_StartGame()   */
+       /* to set up list based on math_opts               */
+  {
+    #ifdef MC_DEBUG
+    printf("\nNo wrong questions to review!\n");
+    printf("\nLeaving MC_StartGameUsingWrongs()\n");
+    #endif
+
+    return MC_StartGame();
+  }
+}
+
+
+/*  MC_NextQuestion() takes a pointer to an allocated     */
+/*  MC_MathQuestion struct and fills in the fields for    */
+/*  use by the user interface program. It basically is    */
+/*  like taking the next flashcard from the pile. The     */
+/*  node containing the question is removed from the list.*/
+/*  Returns 1 if question found, 0 if list empty/invalid  */
+/*  or if argument pointer is invalid.                    */
+int MC_NextQuestion(MC_FlashCard* fc)
+{
+  #ifdef MC_DEBUG
+  printf("\nEntering MC_NextQuestion()");
+  #endif
+
+  /* (so we can free the node after removed from list:) */
+  MC_MathQuestion* ptr;
+  ptr = question_list;
+
+  if (!fc )
+  {
+    fprintf(stderr, "\nInvalid MC_FlashCard* argument!\n");
+
+    #ifdef MC_DEBUG
+    printf("\nInvalid MC_FlashCard* argument!");
+    printf("\nLeaving MC_NextQuestion()\n");
+    #endif
+
+    return 0;
+  }
+
+  if (!question_list ||
+/*      !next_question || */
+      !list_length(question_list) )
+  {
+    #ifdef MC_DEBUG
+    printf("\nquestion_list invalid or empty");
+    printf("\nLeaving MC_NextQuestion()\n");
+    #endif
+
+    return 0;
+  }
+
+  fc->num1 = question_list->card.num1;
+  fc->num2 = question_list->card.num2;
+  fc->num3 = question_list->card.num3;
+  fc->operation = question_list->card.operation;
+  fc->format = question_list->card.format;
+  
+  /* take first question node out of list and free it:   */
+  question_list = remove_node(question_list, question_list);
+  free(ptr);
+  quest_list_length--;
+  questions_pending++;
+
+  #ifdef MC_DEBUG
+  printf("\nnext question is:");
+  print_card(*fc);
+  print_counters();
+  printf("\nLeaving MC_NextQuestion()\n");
+  #endif
+
+  return 1;
+}
+
+/*  MC_AnsweredCorrectly() is how the user interface      */
+/*  tells MathCards that the question has been answered   */
+/*  correctly. Returns 1 if no errors.                    */
+int MC_AnsweredCorrectly(MC_FlashCard* fc)
+{
+  #ifdef MC_DEBUG
+  printf("\nEntering MC_AnsweredCorrectly()");
+  #endif
+
+  if (!fc)
+  {
+    fprintf(stderr, "\nMC_AnsweredCorrectly() passed invalid pointer as argument!\n");
+
+    #ifdef MC_DEBUG
+    printf("\nInvalid MC_FlashCard* argument!");
+    printf("\nLeaving MC_AnsweredCorrectly()\n");
+    #endif
+
+    return 0;
+  }
+
+  #ifdef MC_DEBUG
+  printf("\nQuestion was:");
+  print_card(*fc);
+  #endif
+  
+  answered_correctly++;
+questions_pending--;
+
+  if (math_opts->recycle_corrects)
+  /* reinsert question into question list at random location */
+  {
+    #ifdef MC_DEBUG
+    printf("\nReinserting question into list");
+    #endif
+
+    MC_MathQuestion* ptr1;
+    MC_MathQuestion* ptr2;
+    /* make new node using values from flashcard */
+    ptr1 = create_node_from_card(fc);
+    /* put it into list */
+    ptr2 = pick_random(quest_list_length, question_list);
+    question_list = insert_node(question_list, ptr2, ptr1);
+    quest_list_length++;
+    /* unanswered does not change - was not decremented when */
+    /* question allocated!                                   */
+  }
+  else
+  {
+    #ifdef MC_DEBUG
+    printf("\nNot reinserting question into list");
+    #endif
+    /* not recycling questions so fewer questions remain:      */
+    unanswered--;
+  }
+
+  #ifdef MC_DEBUG
+  print_counters();
+  printf("\nLeaving MC_AnsweredCorrectly()\n");
+  #endif
+
+  return 1;
+}
+
+/*  MC_AnsweredIncorrectly() is how the user interface    */
+/*  tells MathCards that the question has been answered   */
+/*  incorrectly. Returns 1 if no errors.                  */
+int MC_AnsweredIncorrectly(MC_FlashCard* fc)
+{
+  #ifdef MC_DEBUG
+  printf("\nEntering MC_AnsweredIncorrectly()");
+  #endif
+
+  if (!fc)
+  {
+    fprintf(stderr, "\nMC_AnsweredIncorrectly() passed invalid pointer as argument!\n");
+
+    #ifdef MC_DEBUG
+    printf("\nInvalid MC_FlashCard* argument!");
+    printf("\nLeaving MC_AnsweredIncorrectly()\n");
+    #endif
+
+    return 0;
+  }
+
+  #ifdef MC_DEBUG
+  printf("\nQuestion was:");
+  print_card(*fc);
+  #endif
+
+  answered_wrong++;
+  questions_pending--;
+
+  /* add question to wrong_quests list: */
+  MC_MathQuestion* ptr1;
+  MC_MathQuestion* ptr2;
+
+  ptr1 = create_node_from_card(fc);
+
+  if (!already_in_list(wrong_quests, ptr1)) /* avoid duplicates */
+  {
+    #ifdef MC_DEBUG
+    printf("\nAdding to wrong_quests list");
+    #endif
+
+    wrong_quests = append_node(wrong_quests, ptr1);
+  }  
+  else /* avoid memory leak */
+  {
+    free(ptr1);
+  }
+
+  /* if desired, put question back in list so student sees it again */
+  if (math_opts->recycle_wrongs)
+  {
+    int i;
+
+    #ifdef MC_DEBUG
+    printf("\nAdding %d copies to question_list:", math_opts->copies_recycled_wrongs);
+    #endif
+ 
+    /* can put in more than one copy (to drive the point home!) */
+    for (i = 0; i < math_opts->copies_recycled_wrongs; i++)
+    {  
+      ptr1 = create_node_from_card(fc);
+      ptr2 = pick_random(quest_list_length, question_list);
+      question_list = insert_node(question_list, ptr2, ptr1);
+      quest_list_length++;
+    }
+    /* unanswered stays the same if a single copy recycled or */
+    /* increases by 1 for each "extra" copy reinserted:       */
+    unanswered += (math_opts->copies_recycled_wrongs - 1);
+  }
+  else
+  {
+    #ifdef MC_DEBUG
+    printf("\nnot recycling wrong answers\n");
+    #endif
+
+    /* not recycling questions so list gets shorter:      */
+    unanswered--;
+  }
+
+  #ifdef MC_DEBUG
+  print_counters();
+  printf("\nLeaving MC_Answered_Incorrectly()\n");
+  #endif
+
+  return 1;
+}
+
+
+/* Tells user interface if all questions have been answered correctly! */
+/* Requires that at list contained at least one question to start with */
+/* and that wrongly answered questions have been recycled.             */
+int MC_MissionAccomplished(void)
+{
+  if (starting_length
+    && math_opts->recycle_wrongs
+    && !unanswered)
+  {
+    return 1;
+  }
+  else
+  {
+    return 0;
+  }
+}
+
+/*  Returns 1 if no more questions left (either in list   */
+/*  or "in play"                                          */
+int MC_GameOver(void)
+{
+  return !unanswered;
+}
+
+/*  Returns 1 if no more questions left in list, NOT    */
+/*  including questions currently "in play".            */
+int MC_NoQuestionsLeft(void)
+{
+  return !quest_list_length;
+}
+
+
+/* Frees heap memory used in program:                   */
+void MC_EndGame(void)
+{
+  delete_list(question_list);
+  question_list = 0;
+  delete_list(wrong_quests);
+  wrong_quests = 0;
+
+  if (math_opts)
+  {
+    free(math_opts);
+    math_opts = 0;
+  }
+
+  initialized = 0;
+}
+
+
+/* Simple Get()- and Set()-style functions for math options settings: */
+
+
+/* Set general math options: */
+void MC_SetMaxAnswer(int max)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetMaxAnswer(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_answer = sane_value(max);
+}
+
+
+void MC_SetAllowNegAnswer(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAllowNegAnswer(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->allow_neg_answer = int_to_bool(opt);
+}
+
+
+void MC_SetRecycleCorrects(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetRecycleCorrects(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->recycle_corrects = int_to_bool(opt);
+}
+
+
+void MC_SetRecycleWrongs(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetRecycleWrongs(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->recycle_wrongs = int_to_bool(opt);
+}
+
+
+void MC_SetCopiesRecycledWrongs(int copies)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetCopiesRecycledWrongs(): math_opts not valid!\n");
+    return;
+  }
+  /* number of copies must be between 1 and 10: */
+  if (copies < 1)
+    copies = 1;
+  if (copies > 10)
+    copies = 10;
+  math_opts->copies_recycled_wrongs = copies;
+}
+
+/*NOTE - list can contain more than one format */
+void MC_SetFormatAnswerLast(int opt)       /* Enable questions like:  a + b = ?    */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetFormatAnswerLast(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->format_answer_last = int_to_bool(opt);
+} 
+
+
+void MC_SetFormatAnswerFirst(int opt)      /* Enable questions like:  ? + b = c   */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetFormatAnswerFirst(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->format_answer_first = int_to_bool(opt);
+}
+
+ 
+void MC_SetFormatAnswerMiddle(int opt)     /* Enable questions like:  a + ? = c   */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetFormatAnswerMiddle(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->format_answer_middle = int_to_bool(opt);
+} 
+
+
+void MC_SetQuestionCopies(int copies)      /* how many times each question is put in list */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetQuestionCopies(): math_opts not valid!\n");
+    return;
+  }
+  /* number of copies must be between 1 and 10: */
+  if (copies < 1)
+    copies = 1;
+  if (copies > 10)
+    copies = 10;
+  math_opts->question_copies = copies;
+}
+
+
+void MC_SetRandomize(int opt)   
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetRandomize(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->randomize = int_to_bool(opt);
+} 
+
+
+/* Set math operations to be used in game: */
+void MC_SetAddAllowed(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAddAllowed(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->addition_allowed = int_to_bool(opt);
+}
+
+
+void MC_SetSubAllowed(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetSubAllowed(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->subtraction_allowed = int_to_bool(opt);
+}
+
+
+void MC_SetMultAllowed(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetMultAllowed(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->multiplication_allowed = int_to_bool(opt);
+}
+
+
+void MC_SetDivAllowed(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetDivAllowed(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->division_allowed = int_to_bool(opt);
+}
+
+
+
+
+
+/* Set min and max for addition: */
+void MC_SetAddMin(int opt)
+{
+  MC_SetAddMinAugend(opt);
+  MC_SetAddMinAddend(opt);
+}
+
+
+void MC_SetAddMinAugend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAddMinAugend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_augend = sane_value(opt);
+}
+
+
+void MC_SetAddMinAddend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAddMinAddend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_addend = sane_value(opt);
+}
+
+
+void MC_SetAddMax(int opt)
+{
+  MC_SetAddMaxAugend(opt);
+  MC_SetAddMaxAddend(opt);
+}
+
+
+void MC_SetAddMaxAugend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAddMaxAugend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_augend = sane_value(opt);
+}
+
+
+void MC_SetAddMaxAddend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAddMaxAddend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_addend = sane_value(opt);
+}
+
+
+
+
+/* Set min and max for subtraction: */
+void MC_SetSubMin(int opt)
+{
+  MC_SetSubMinMinuend(opt);
+  MC_SetSubMinSubtrahend(opt);
+}
+
+
+void MC_SetSubMinMinuend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MC_SetSubMinMinuend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_minuend = sane_value(opt);
+}
+
+
+void MC_SetSubMinSubtrahend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetSubMinSubtrahend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_subtrahend = sane_value(opt);
+}
+
+
+void MC_SetSubMax(int opt)
+{
+  MC_SetSubMaxMinuend(opt);
+  MC_SetSubMaxSubtrahend(opt);
+}
+
+
+void MC_SetSubMaxMinuend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetSubMaxMinuend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_minuend = sane_value(opt);
+}
+
+
+void MC_SetSubMaxSubtrahend(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetSubMaxSubtrahend(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_subtrahend = sane_value(opt);
+}
+
+
+
+
+/* Set min and max for multiplication: */
+void MC_SetMultMin(int opt)
+{
+  MC_SetMultMinMultiplier(opt);
+  MC_SetMultMinMultiplicand(opt);
+}
+
+
+void MC_SetMultMinMultiplier(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetMultMinMultiplier(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_multiplier = sane_value(opt);
+}
+
+
+void MC_SetMultMinMultiplicand(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetMultMinMultiplicand(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_multiplicand = sane_value(opt);
+}
+
+
+void MC_SetMultMax(int opt)
+{
+  MC_SetMultMaxMultiplier(opt);
+  MC_SetMultMaxMultiplicand(opt);
+}
+
+
+void MC_SetMultMaxMultiplier(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetMultMaxMultiplier(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_multiplier = sane_value(opt);
+}
+
+
+void MC_SetMultMaxMultiplicand(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetMultMaxMultiplicand(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_multiplicand = sane_value(opt);
+}
+
+
+
+
+/* Set min and max for division: */
+void MC_SetDivMin(int opt)
+{
+  MC_SetDivMinDivisor(opt);
+  MC_SetDivMinQuotient(opt);
+}
+
+
+void MC_SetDivMinDivisor(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetDivMinDivisor(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_divisor = sane_value(opt);
+}
+
+
+void MC_SetDivMinQuotient(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetDivMinQuotient(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->min_quotient = sane_value(opt);
+}
+
+
+void MC_SetDivMax(int opt)
+{
+  MC_SetDivMaxDivisor(opt);
+  MC_SetDivMaxQuotient(opt);
+}
+
+
+void MC_SetDivMaxDivisor(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetDivMaxDivisor(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_divisor = sane_value(opt);
+}
+
+
+void MC_SetDivMaxQuotient(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetDivMaxQuotient(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->max_quotient = sane_value(opt);
+}
+
+
+
+
+/*"Get" type methods to query option parameters */
+
+/* Query general math options: */
+int MC_MaxAnswer(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MaxAnswer(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_answer;
+}
+
+int MC_AllowNegAnswer(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_AllowNegAnswer(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->allow_neg_answer;
+}
+
+
+int MC_RecycleCorrects(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_RecycleCorrects(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->recycle_corrects;
+}
+
+
+int MC_RecycleWrongs(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_RecycleWrongs(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->recycle_wrongs;
+}
+
+
+int MC_CopiesRecycledWrongs(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_CopiesRecycledWrongs(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->copies_recycled_wrongs;
+}
+
+
+int MC_FormatAnswerLast(void)      /* a + b = ?                                               */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_FormatAnswerLast(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->format_answer_last;
+} 
+
+
+int MC_FormatAnswerFirst(void)     /* ? + b = c  NOTE - list can contain more than one format */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_FormatAnswerFirst(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->format_answer_first;
+} 
+
+
+int MC_FormatAnswerMiddle(void)    /* a + ? = c                                               */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_FormatAnswerMiddle(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->format_answer_middle;
+} 
+
+
+int MC_QuestionCopies(void)         /* how many times each question is put in list */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_QuestionCopies(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->question_copies;
+} 
+
+
+int MC_Randomize(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_Randomize(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->randomize;
+} 
+
+
+
+/* Query the allowed math operations: */
+int MC_AddAllowed(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_AddAllowed(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->addition_allowed;
+}
+
+
+int MC_SubAllowed(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SubAllowed(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->subtraction_allowed;
+}
+
+
+int MC_MultAllowed(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MultAllowed(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->multiplication_allowed;
+}
+
+
+int MC_DivAllowed(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_DivAllowed(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->division_allowed;
+}
+
+
+
+/* Query min and max for addition: */
+int MC_AddMinAugend(void)               /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_AddMinAugend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_augend;
+}
+
+
+int MC_AddMinAddend(void)               /* options for the other addend */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_AddMinAddend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_addend;
+}
+
+
+int MC_AddMaxAugend(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_AddMaxAugend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_augend;
+}
+
+
+int MC_AddMaxAddend(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_AddMaxAddend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_addend;
+}
+
+
+
+/* Query min and max for subtraction: */
+int MC_SubMinMinuend(void)              /* minuend - subtrahend = difference */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SubMinMinuend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_minuend;
+}
+
+
+int MC_SubMinSubtrahend(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SubMinSubtrahend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_subtrahend;
+}
+
+
+
+int MC_SubMaxMinuend(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SubMaxMinuend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_minuend;
+}
+
+
+
+int MC_SubMaxSubtrahend(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SubMaxSubtrahend(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_subtrahend;
+}
+
+
+
+/* Query min and max for multiplication: */
+int MC_MultMinMultiplier(void)          /* multiplier * multiplicand = product */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MultMinMultiplier(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_multiplier;
+}
+
+
+int MC_MultMinMultiplicand(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MultMinMultiplicand(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_multiplicand;
+}
+
+
+
+int MC_MultMaxMultiplier(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MultMaxMultiplier(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_multiplier;
+}
+
+
+
+int MC_MultMaxMultiplicand(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_MultMaxMultiplicand(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_multiplicand;
+}
+
+
+
+/* Query min and max for division: */
+int MC_DivMinDivisor(void)             /* dividend/divisor = quotient */
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_DivMinDivisor(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_divisor;
+}
+
+
+int MC_DivMinQuotient(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_DivMinQuotient(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->min_quotient;
+}
+
+
+int MC_DivMaxDivisor(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_DivMaxDivisor(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_divisor;
+}
+
+
+int MC_DivMaxQuotient(void)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_DivMaxQuotient(): math_opts not valid!\n");
+    return MC_MATH_OPTS_INVALID;
+  }
+  return math_opts->max_quotient;
+}
+
+
+
+/* Implementation of "private methods" - (cannot be called from outside
+of this file) */
+
+
+/* using parameters from the mission struct, create linked list of "flashcards" */
+/* FIXME should figure out how to proceed correctly if we run out of memory */
+/* FIXME implement question formats and question_copies flags */
+/* FIXME handle max_answer correctly, even if negative */
+MC_MathQuestion* generate_list(void)
+{
+  MC_MathQuestion* top_of_list = 0;
+  MC_MathQuestion* end_of_list = 0;
+  MC_MathQuestion* tmp_ptr = 0;
+
+  int i, j;
+  int length = 0;
+
+  #ifdef MC_DEBUG
+  printf("\nEntering generate_list()");
+  print_math_options();
+  #endif
+ 
+  /* add nodes for each math operation allowed */
+  if (math_opts->addition_allowed)
+  {
+    for (i = math_opts->min_augend; i <= math_opts->max_augend; i++)
+    {
+      for (j = math_opts->min_addend; j <= math_opts->max_addend; j++)
+      {
+       /* prevent negative numbers if desired */
+        if (math_opts->allow_neg_answer || ((i >= 0) && (j >=0)))
+        {  
+          /* make sure max_questions not exceeded */
+          if (length < math_opts->max_questions)
+          {
+            tmp_ptr = create_node(i, j, MC_OPER_ADD, i + j, MC_FORMAT_ANS_LAST);
+            top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+            end_of_list = tmp_ptr;
+            length++; 
+          } 
+        }
+      }
+    }
+  }
+
+  if (math_opts->subtraction_allowed)
+  {
+    for (i = math_opts->min_minuend; i <= math_opts->max_minuend; i++)
+    {
+      for (j = math_opts->min_subtrahend; j <= math_opts->max_subtrahend; j++)
+      {
+        /* prevent negative numbers if desired */
+        if (math_opts->allow_neg_answer || (i >= j))
+        { 
+          /* make sure max_questions not exceeded */
+          if (length < math_opts->max_questions)
+          {
+            tmp_ptr = create_node(i, j, MC_OPER_SUB, i - j, MC_FORMAT_ANS_LAST);
+            top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+            end_of_list = tmp_ptr;
+            length++;
+          } 
+        }
+      }
+    }
+  }
+
+  if (math_opts->multiplication_allowed)
+  {
+    for (i = math_opts->min_multiplier; i <= math_opts->max_multiplier; i++)
+    {
+      for (j = math_opts->min_multiplicand; j <= math_opts->max_multiplicand; j++)
+      { 
+       /* prevent negative numbers if desired */
+        if (math_opts->allow_neg_answer || ((i >= 0) && (j >=0)))
+        {
+          /* make sure max_questions not exceeded */
+          if (length < math_opts->max_questions)
+          {
+            tmp_ptr = create_node(i, j, MC_OPER_MULT, i * j, MC_FORMAT_ANS_LAST);
+            top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+            end_of_list = tmp_ptr;
+            length++;
+          }
+        }
+      }
+    }
+  }
+
+  if (math_opts->division_allowed)
+  {
+    for (i = math_opts->min_quotient; i <= math_opts->max_quotient; i++)
+    {
+      for (j = math_opts->min_divisor; j <= math_opts->max_divisor; j++)
+      {
+       /* prevent negative numbers if desired */
+        if (math_opts->allow_neg_answer || ((i >= 0) && (j >=0)))
+        {
+          if (j) /* prevent division by zero */
+          {
+            /* make sure max_questions not exceeded */
+            if (length < math_opts->max_questions)
+            {
+              tmp_ptr = create_node(i * j, j, MC_OPER_DIV, i, MC_FORMAT_ANS_LAST);
+              top_of_list = insert_node(top_of_list, end_of_list, tmp_ptr);
+              end_of_list = tmp_ptr;
+              length++;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  #ifdef MC_DEBUG
+  length = list_length(top_of_list); 
+  printf("\nlength before randomization:\t%d", length); 
+  #endif
+
+  /*  now shuffle list if desired: */
+  if (math_opts->randomize)
+  {
+    top_of_list = randomize_list(top_of_list); 
+  }
+
+  #ifdef MC_DEBUG
+  length = list_length(top_of_list); 
+  printf("\nlength after randomization:\t%d", length); 
+  printf("\nLeaving generate_list()\n");
+  #endif
+
+  return top_of_list;
+}
+
+
+
+
+/* create a new node and return a pointer to it */
+MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f)
+{
+  MC_MathQuestion* ptr;
+  ptr = malloc(sizeof(MC_MathQuestion));
+  ptr->card.num1 = n1;
+  ptr->card.num2 = n2;  
+  ptr->card.num3 = ans;
+  ptr->card.operation = op;
+  ptr->card.format = f;
+  ptr->next = 0;
+  ptr->previous =0;
+  return ptr;
+}
+
+
+
+/* a "copy constructor", so to speak */
+/* FIXME should properly return newly allocated list if more than one node DSB */
+MC_MathQuestion* create_node_copy(MC_MathQuestion* other)
+{
+  MC_MathQuestion* ptr;
+  if (!other)
+    return 0;
+  ptr = malloc(sizeof(MC_MathQuestion));
+  ptr->card.num1 = other->card.num1;
+  ptr->card.num2 = other->card.num2;
+  ptr->card.num3 = other->card.num3;
+  ptr->card.operation = other->card.operation;
+  ptr->card.format = other->card.format;
+  ptr->next = 0;
+  ptr->previous = 0;
+  return ptr;
+}
+
+
+
+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;
+}
+
+
+
+MC_FlashCard* create_card_from_node(MC_MathQuestion* node)
+{
+  MC_FlashCard* fc;
+  if (!node)
+    return 0;
+  fc = malloc(sizeof(MC_FlashCard));
+  fc->num1 = node->card.num1;
+  fc->num2 = node->card.num2;
+  fc->num3 = node->card.num3;
+  fc->operation = node->card.operation;
+  fc->format = node->card.format;
+  return fc;
+}
+
+
+
+/* this one copies the contents, including pointers; both nodes must be allocated */
+int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
+{
+  if (!original || !copy)
+  {
+    printf("\nIn copy_node(): invalid pointer as argument.\n");
+    fprintf(stderr, "\nIn copy_node(): invalid pointer as argument.\n");
+    return 0;
+  }  
+  copy->card.num1 = original->card.num1;
+  copy->card.num2 = original->card.num2;
+  copy->card.num3 = original->card.num3;
+  copy->card.operation = original->card.operation;
+  copy->card.format = original->card.format;
+  copy->next = original->next;
+  copy->previous = original->previous;
+  return 1;
+}
+
+
+
+/* this puts the node into the list AFTER the node pointed to by current */
+/* and returns a pointer to the top of the modified list  */
+MC_MathQuestion* insert_node(MC_MathQuestion* first, MC_MathQuestion* current, MC_MathQuestion* new_node)
+{
+  /* return pointer to list unchanged if new_node doesn't exist*/
+  if (!new_node)
+    return first;
+  /* if current doesn't exist, new_node is first */
+  if (!current)
+  {
+    new_node->previous = 0;
+    new_node->next =0;
+    first = new_node;
+    return first;
+  }
+
+  if (current->next)  /* avoid error if at end of list */
+    current->next->previous = new_node;
+  new_node->next = current->next;
+  current->next = new_node;
+  new_node->previous = current;
+  return first;
+}
+
+
+
+/* adds the new node to the end of the list */
+MC_MathQuestion* append_node(MC_MathQuestion* list, MC_MathQuestion* new_node)
+{
+  MC_MathQuestion* ptr;
+  /* return pointer to list unchanged if new_node doesn't exist*/
+  if (!new_node)
+  {
+    return list;
+  }
+
+  /* if list does not exist, new_node is the first (and only) node */
+  if (!list)
+  {
+    return new_node;
+  }
+  /* otherwise, go to end of list */
+  ptr = list;
+  while (ptr->next)
+  {
+    ptr = ptr->next;
+  }
+
+  ptr->next = new_node;
+  new_node->previous = ptr;
+  new_node->next = 0;
+  return list;
+}
+
+
+
+/* this takes the node out of the list but does not delete it */
+/* and returns a pointer to the top of the modified list  */
+MC_MathQuestion* remove_node(MC_MathQuestion* first, MC_MathQuestion* n)
+{
+  if (!n || !first)
+    return first;
+  /* special case if first node being removed */
+  if (n == first)
+     first = first->next;
+
+  if (n->previous)
+    n->previous->next = n->next;
+  if (n->next)
+      n->next->previous = n->previous;
+  n->previous = 0;
+  n->next = 0;
+  return first;
+}
+
+
+
+/* frees memory for entire list and returns null pointer */
+MC_MathQuestion* delete_list(MC_MathQuestion* list)
+{
+  MC_MathQuestion* tmp_ptr;
+  while (list)
+  {
+    tmp_ptr = list->next; 
+    free (list);
+    list = tmp_ptr;
+  }
+  return list;
+}
+
+
+
+/* prints struct to stdout for testing purposes */
+void print_math_options(void)
+{
+  printf("\nprint_math_options():\n");
+
+ /* bail out if no struct */
+  if (!math_opts)
+  {
+    printf("\nMath Options struct does not exist!\n");
+    return;
+  }
+
+  printf("\nGeneral math options:\n");
+  printf("allow_neg_answer:\t%d\n", math_opts->allow_neg_answer);
+  printf("max_answer:\t%d\n", math_opts->max_answer);
+  printf("max_questions:\t%d\n", math_opts->max_questions);
+  printf("format_answer_last:\t%d\n", math_opts->format_answer_last);
+  printf("format_answer_first:\t%d\n", math_opts->format_answer_first);
+  printf("format_answer_middle:\t%d\n", math_opts->format_answer_middle);
+  printf("question_copies:\t%d\n", math_opts->question_copies);
+  printf("randomize:\t%d\n", math_opts->randomize);
+
+  printf("\nSpecific math operation options:\n");
+  printf("addition_allowed:\t%d\n", math_opts->addition_allowed);
+  printf("min_augend:\t%d\n", math_opts->min_augend);
+  printf("max_augend:\t%d\n", math_opts->max_augend);
+  printf("min_addend:\t%d\n", math_opts->min_addend);
+  printf("max_addend:\t%d\n", math_opts->max_addend);
+
+  printf("subtraction_allowed\t%d\n", math_opts->subtraction_allowed);
+  printf("min_minuend:\t%d\n", math_opts->min_minuend);
+  printf("max_minuend:\t%d\n", math_opts->max_minuend);
+  printf("min_subtrahend:\t%d\n", math_opts->min_subtrahend);
+  printf("max_subtrahend:\t%d\n", math_opts->max_subtrahend);
+
+  printf("multiplication_allowed:\t%d\n", math_opts->multiplication_allowed);
+  printf("min_multiplier:\t%d\n", math_opts->min_multiplier);
+  printf("max_multiplier:\t%d\n", math_opts->max_multiplier);
+  printf("min_multiplicand:\t%d\n", math_opts->min_multiplicand);
+  printf("max_multiplicand:\t%d\n", math_opts->max_multiplicand);
+
+  printf("division_allowed:\t%d\n", math_opts->division_allowed);
+  printf("min_divisor:\t%d\n",math_opts->min_divisor);
+  printf("max_divisor:\t%d\n", math_opts->max_divisor);
+  printf("min_quotient:\t%d\n", math_opts->min_quotient);
+  printf("max_quotient:\t%d\n", math_opts->max_quotient);
+}
+
+
+
+void print_list(MC_MathQuestion* list)
+{
+  if (!list)
+  {
+    printf("\nprint_list(): list empty or pointer invalid\n");
+    return;
+  }
+  MC_MathQuestion* ptr = list;
+  printf("\nprint_list() printing list:");
+  printf("\nlist_length():\t%d", list_length(list));
+  while (ptr)
+  {
+    print_node(ptr);
+    ptr = ptr->next;
+  }
+}
+
+
+
+void print_node(MC_MathQuestion* ptr)
+{
+  if (ptr)
+  printf("\n%d,  %d \tOper %d \tAnswer %d",
+           ptr->card.num1,
+           ptr->card.num2,
+           ptr->card.operation,
+           ptr->card.num3);
+}
+
+
+
+void print_card(MC_FlashCard card)
+{
+  printf("\nprint_card():");
+  printf("\n%d,  %d \tOper %d \tAnswer %d \t Format %d\n",
+           card.num1,
+           card.num2,
+           card.operation,
+           card.num3,
+           card.format);
+}
+
+
+
+
+/* This sends the values of all "global" counters and the */
+/* lengths of the question lists to stdout - for debugging */
+void print_counters(void)
+{
+  printf("\nquest_list_length = \t%d", quest_list_length);
+  printf("\nlist_length(question_list) = \t%d", list_length(question_list));
+  printf("\nstarting_length = \t%d", starting_length);
+  printf("\nunanswered = \t%d", unanswered);
+  printf("\nanswered_correctly = \t%d", answered_correctly);
+  printf("\nanswered_wrong = \t%d", answered_wrong);
+  printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
+  printf("\nquestions_pending = \t%d", questions_pending);
+}
+
+int list_length(MC_MathQuestion* list)
+{
+  int length = 0;
+  while (list)
+  {
+    length++;
+    list = list->next;
+  }
+  return length;
+}
+
+
+
+
+MC_MathQuestion* randomize_list(MC_MathQuestion* old_list)
+{
+
+  MC_MathQuestion* old_tmp = 0;
+  MC_MathQuestion* new_list = 0;
+  MC_MathQuestion* new_tmp =0;
+
+  int old_length = list_length(old_list);
+  int new_length = 0;
+
+  #ifdef MC_DEBUG
+  printf("\nEntering randomize_list()");
+  printf("\nBefore randomization:");
+  printf("\nPrinting old_list:");
+  print_list(old_list);
+  printf("\nPrinting new_list:");
+  print_list(new_list);
+  #endif
+
+
+  while (old_length && old_list)
+  {
+    old_tmp = pick_random(old_length, old_list);
+    new_tmp = pick_random(new_length, new_list);
+
+    if (old_tmp)
+    {
+      old_list = remove_node(old_list, old_tmp);
+      new_list = insert_node(new_list, new_tmp, old_tmp);
+      old_length--;
+      new_length++;
+    }
+    else
+    {
+      #ifdef MC_DEBUG
+      printf("\nUnexpected exit!");
+      printf("\nAfter randomization:");
+      printf("\nPrinting old_list:");
+      print_list(old_list);
+      printf("\nPrinting new_list:");
+      print_list(new_list);
+      printf("\nLeaving randomize_list()");
+      #endif
+
+      return new_list;
+    }
+  }
+
+  #ifdef MC_DEBUG
+  printf("\nAfter randomization:");
+  printf("\nPrinting old_list:");
+  print_list(old_list);
+  printf("\nPrinting new_list:");
+  print_list(new_list);
+  printf("\nLeaving randomize_list()");
+  #endif
+
+  return new_list;
+}
+
+MC_MathQuestion* pick_random(int length, MC_MathQuestion* list)
+{
+  int i;
+  int rand_node;
+
+  /* set random seed DSB */
+  srand(time(0));  
+
+  /* if length is zero, get out to avoid divide-by-zero error */
+  if (0 == length)
+  {
+    return list;
+  }
+
+  rand_node = rand() % length;
+
+  for (i=1; i < rand_node; i++)
+  {
+    if (list)
+     list = list->next;
+  }
+
+  return list;
+}
+
+/* compares fields other than pointers */
+int compare_node(MC_MathQuestion* first, MC_MathQuestion* other)
+{
+  if (!first || !other)
+    return 0;
+  if (first->card.num1 == other->card.num1
+   && first->card.num2 == other->card.num2
+   && first->card.operation == other->card.operation
+   && first->card.format == other->card.format)
+    return 1;
+  else
+    return 0;
+}
+
+/* check to see if list already contains an identical node */
+int already_in_list(MC_MathQuestion* list, MC_MathQuestion* ptr)
+{
+  if (!list || !ptr)
+    return 0;
+
+  while (list)
+  {
+    if (compare_node(list, ptr))
+      return 1;
+    list = list->next;
+  }
+  return 0;
+}
+
+/* to prevent option settings in math_opts from getting set to */
+/* values other than 0 or 1                                    */
+int int_to_bool(int i)
+{
+  if (i)
+    return 1;
+  else
+    return 0;
+}
+
+/* prevent values from getting into math_opts that are outside */
+/* the range that can be handled by the program (i.e. more     */
+/* than three digits; also disallow negatives if that has been */
+/* selected.                                                   */
+int sane_value(int i)
+{
+  if (i > MC_GLOBAL_MAX)
+    i = MC_GLOBAL_MAX;
+  else if (i < -MC_GLOBAL_MAX)
+    i = -MC_GLOBAL_MAX;
+  
+  if (i < 0 
+   && math_opts
+   && !math_opts->allow_neg_answer)
+  {
+    i = 0;
+  }
+
+  return i;
+}
+
+int abs_value(int i)
+{
+  if (i > 0)
+    return i;
+  else
+    return -i;
+}

Added: tuxmath/trunk/src/mathcards.h
===================================================================
--- tuxmath/trunk/src/mathcards.h	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/mathcards.h	2006-05-16 17:05:20 UTC (rev 10)
@@ -0,0 +1,313 @@
+/*
+
+	mathcards.h
+	
+	Description: contains headers for a flashcard-type math game. 
+        This is a sort of interface-independent backend that could be used with a different
+        user interface. Developed as an enhancement to Bill Kendrick's "Tux of Math Command"
+        (aka tuxmath).  If tuxmath were a C++ program, this would be a C++ class.
+	
+	Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
+	
+	Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL version 2 or later)
+
+*/
+#ifndef MATHCARDS_H
+#define MATHCARDS_H
+
+#undef MC_DEBUG
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+
+/* type of math operation used in a given question */
+enum {
+  MC_OPER_ADD,
+  MC_OPER_SUB,
+  MC_OPER_MULT,
+  MC_OPER_DIV,
+  MC_NUM_OPERS
+};
+
+/* math question formats: */
+enum {
+  MC_FORMAT_ANS_LAST,     /* a + b = ? */
+  MC_FORMAT_ANS_FIRST,    /* ? + b = c */
+  MC_FORMAT_ANS_MIDDLE    /* a + ? = c */
+};
+
+/* This struct contains all options that determine what */
+/* math questions are asked during a game */
+typedef struct MC_Options {
+  /* general math options */
+  int allow_neg_answer;
+  int max_answer;
+  int max_questions;
+  int recycle_corrects;
+  int recycle_wrongs;
+  int copies_recycled_wrongs;
+  int format_answer_last;      /* a + b = ?                                               */ 
+  int format_answer_first;     /* ? + b = c  NOTE - list can contain more than one format */
+  int format_answer_middle;    /* a + ? = c                                               */
+  int question_copies;         /* how many times each question is put in list */
+  int randomize;               /* whether to shuffle cards */
+  /* addition options */
+  int addition_allowed;
+  int min_augend;              /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+  int max_augend;
+  int min_addend;              /* options for the other addend */
+  int max_addend;
+  /* subtraction options */
+  int subtraction_allowed;
+  int min_minuend;             /* minuend - subtrahend = difference */
+  int max_minuend;
+  int min_subtrahend;
+  int max_subtrahend;
+  /* multiplication options */
+  int multiplication_allowed;
+  int min_multiplier;          /* multiplier * multiplicand = product */
+  int max_multiplier;
+  int min_multiplicand;
+  int max_multiplicand;
+  /* division options */
+  int division_allowed;
+  int min_divisor;             /* dividend/divisor = quotient */
+  int max_divisor;
+  int min_quotient;
+  int max_quotient;
+} MC_Options;
+
+/* default values for math_options */
+#define MC_GLOBAL_MAX 999                 /* this is the largest absolute value that can be entered  */
+                                          /* as a max or min for math question values.               */
+#define MC_MATH_OPTS_INVALID -9999        /* return value for accessor functions if math_opts not valid    */
+
+#define DEFAULT_ALLOW_NEG_ANSWER 0
+#define DEFAULT_MAX_ANSWER 144
+#define DEFAULT_MAX_QUESTIONS 5000
+#define DEFAULT_RECYCLE_CORRECTS 1        /* reuse correctly answered questions or not */
+#define DEFAULT_RECYCLE_WRONGS   1        /* reuse incorrectly answered questions or not */
+#define DEFAULT_COPIES_RECYCLED_WRONGS 1  /* how many copies of an incorrectly answered question to re-insert*/
+#define DEFAULT_FORMAT_ANSWER_LAST 1      /* question format is: a + b = ? */
+#define DEFAULT_FORMAT_ANSWER_FIRST 0     /* question format is: ? + b = c */
+#define DEFAULT_FORMAT_ANSWER_MIDDLE 0    /* question format is: a + ? = c */
+#define DEFAULT_QUESTION_COPIES 1         /* how many times each question is put in list */
+#define DEFAULT_RANDOMIZE 1               /* whether to shuffle cards */
+
+#define DEFAULT_ADDITION_ALLOWED 1
+#define DEFAULT_MIN_AUGEND 0              /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+#define DEFAULT_MAX_AUGEND 12
+#define DEFAULT_MIN_ADDEND 0
+#define DEFAULT_MAX_ADDEND 12
+
+#define DEFAULT_SUBTRACTION_ALLOWED 1     /* minuend - subtrahend = difference */
+#define DEFAULT_MIN_MINUEND 0
+#define DEFAULT_MAX_MINUEND 12
+#define DEFAULT_MIN_SUBTRAHEND 0
+#define DEFAULT_MAX_SUBTRAHEND 12
+
+#define DEFAULT_MULTIPLICATION_ALLOWED 1
+#define DEFAULT_MIN_MULTIPLIER 0          /* multiplier * multiplicand = product */
+#define DEFAULT_MAX_MULTIPLIER 12
+#define DEFAULT_MIN_MULTIPLICAND 0
+#define DEFAULT_MAX_MULTIPLICAND 12
+
+#define DEFAULT_DIVISION_ALLOWED 1        /* dividend/divisor = quotient */
+#define DEFAULT_MIN_DIVISOR 0             /* note - generate_list() will prevent */
+#define DEFAULT_MAX_DIVISOR 12             /* questions with division by zero.    */
+#define DEFAULT_MIN_QUOTIENT 0
+#define DEFAULT_MAX_QUOTIENT 12
+
+/* struct for individual "flashcard" */
+typedef struct MC_FlashCard {
+  int num1;
+  int num2;
+  int num3;
+  int operation;
+  int format;
+} MC_FlashCard;
+
+/* struct for node in math "flashcard" list */
+typedef struct MC_MathQuestion {
+  MC_FlashCard card;
+  struct MC_MathQuestion *next;
+  struct MC_MathQuestion *previous;
+} MC_MathQuestion;
+
+/* "public" function prototypes: these functions are how */
+/* a user interface communicates with MathCards:         */
+/* TODO provide comments thoroughly explaining these functions */
+
+
+/*  MC_Initialize() sets up the struct containing all of  */
+/*  settings regarding math questions.  It should be      */
+/*  called before any other function.  Many of the other  */
+/*  functions will not work properly if MC_Initialize()   */
+/*  has not been called. It only needs to be called once, */  
+/*  i.e when the program is starting, not at the beginning*/
+/*  of each math game for the player. Returns 1 if        */
+/*  successful, 0 otherwise.                              */
+int MC_Initialize(void);
+
+/*  MC_StartGame() generates the list of math questions   */
+/*  based on existing settings. It should be called at    */
+/*  the beginning of each math game for the player.       */
+/*  Returns 1 if resultant list contains 1 or more        */
+/*  questions, 0 if list empty or not generated           */
+/*  successfully.                                         */
+int MC_StartGame(void);
+
+/*  MC_StartGameUsingWrongs() is like MC_StartGame(),     */
+/*  but uses the incorrectly answered questions from the  */
+/*  previous game for the question list as a review form  */
+/*  of learning. If there were no wrong answers (or no    */
+/*  previous game), it behaves just like MC_StartGame().  */
+/*  FIXME wonder if it should generate a message if the   */
+/*  list is created from settings because there is no     */
+/*  valid wrong question list?                            */
+int MC_StartGameUsingWrongs(void);
+
+/*  MC_NextQuestion() takes a pointer to an allocated     */
+/*  MC_MathQuestion struct and fills in the fields for    */
+/*  use by the user interface program. It basically is    */
+/*  like taking the next flashcard from the pile.         */
+/*  Returns 1 if question found, 0 if list empty/invalid  */
+/*  or if argument pointer is invalid                     */
+int MC_NextQuestion(MC_FlashCard* q);
+
+/*  MC_AnsweredCorrectly() is how the user interface      */
+/*  tells MathCards that the question has been answered   */
+/*  correctly. Returns 1 if no errors.                    */
+int MC_AnsweredCorrectly(MC_FlashCard* q);
+
+/*  MC_AnsweredIncorrectly() is how the user interface    */
+/*  tells MathCards that the question has been answered   */
+/*  incorrectly. Returns 1 if no errors.                  */
+int MC_AnsweredIncorrectly(MC_FlashCard* q);
+
+/*  Like MC_NextQuestion(), but takes "flashcard" from    */
+/*  pile of incorrectly answered questions.               */
+/*  Returns 1 if question found, 0 if list empty/invalid  */
+int MC_NextWrongQuest(MC_FlashCard* q);
+
+/*  Returns 1 if all have been answered correctly,        */
+/* 0 otherwise.                                           */
+int MC_MissionAccomplished(void);
+
+/*  Returns 1 if no more questions left (either in list   */
+/*  or "in play"                                          */
+/*  FIXME would be better to return number of questions   */
+/*  left so it can be displayed in a counter.             */
+int MC_GameOver(void);
+
+/*  Returns 1 if no more questions left in list, NOT    */
+/*  including questions currently "in play".            */
+int MC_NoQuestionsLeft(void);
+
+/*  Tells MathCards to clean up - should be called when   */
+/*  user interface program exits.                         */
+void MC_EndGame(void);
+
+
+
+/* Simple "Set/Get" type functions for option parameters: */
+
+/* Simple functions to set option parameters: */
+
+/* Set general math options:   */
+void MC_SetMaxAnswer(int max);
+void MC_SetAllowNegAnswer(int opt);
+void MC_SetRecycleCorrects(int opt);
+void MC_SetRecycleWrongs(int opt);
+void MC_SetCopiesRecycledWrongs(int copies);
+void MC_SetFormatAnswerLast(int opt);      /* a + b = ?                                               */ 
+void MC_SetFormatAnswerFirst(int opt);     /* ? + b = c  NOTE - list can contain more than one format */
+void MC_SetFormatAnswerMiddle(int opt);    /* a + ? = c                                               */
+void MC_SetQuestionCopies(int copies);         /* how many times each question is put in list */
+void MC_SetRandomize(int opt);           
+
+/* Set the allowed math operations: */
+void MC_SetAddAllowed(int opt);
+void MC_SetSubAllowed(int opt);
+void MC_SetMultAllowed(int opt);
+void MC_SetDivAllowed(int opt);
+
+/* Set min and max for addition: */
+void MC_SetAddMin(int opt);                    /* augend + addend = sum */
+void MC_SetAddMinAugend(int opt);              /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+void MC_SetAddMinAddend(int opt);              /* options for the other addend */
+void MC_SetAddMax(int opt);
+void MC_SetAddMaxAugend(int opt);
+void MC_SetAddMaxAddend(int opt);
+
+/* Set min and max for subtraction: */
+void MC_SetSubMin(int opt);
+void MC_SetSubMinMinuend(int opt);             /* minuend - subtrahend = difference */
+void MC_SetSubMinSubtrahend(int opt);
+void MC_SetSubMax(int opt);
+void MC_SetSubMaxMinuend(int opt);
+void MC_SetSubMaxSubtrahend(int opt);
+
+/* Set min and max for multiplication: */
+void MC_SetMultMin(int opt);
+void MC_SetMultMinMultiplier(int opt);         /* multiplier * multiplicand = product */
+void MC_SetMultMinMultiplicand(int opt);
+void MC_SetMultMax(int opt);
+void MC_SetMultMaxMultiplier(int opt);
+void MC_SetMultMaxMultiplicand(int opt);
+
+/* Set min and max for division: */
+void MC_SetDivMin(int opt);
+void MC_SetDivMinDivisor(int opt);            /* dividend/divisor = quotient */
+void MC_SetDivMinQuotient(int opt);
+void MC_SetDivMax(int opt);
+void MC_SetDivMaxDivisor(int opt);
+void MC_SetDivMaxQuotient(int opt);
+
+
+/* "Get" type functions to query option parameters: */
+
+/* Query general math options: */
+int MC_MaxAnswer(void);
+int MC_AllowNegAnswer(void);
+int MC_RecycleCorrects(void);
+int MC_RecycleWrongs(void);
+int MC_CopiesRecycledWrongs(void);
+int MC_FormatAnswerLast(void);      /* a + b = ?                                               */ 
+int MC_FormatAnswerFirst(void);     /* ? + b = c  NOTE - list can contain more than one format */
+int MC_FormatAnswerMiddle(void);    /* a + ? = c                                               */
+int MC_QuestionCopies(void);         /* how many times each question is put in list */
+int MC_Randomize(void);         
+
+/* Query the allowed math operations: */
+int MC_AddAllowed(void);
+int MC_SubAllowed(void);
+int MC_MultAllowed(void);
+int MC_DivAllowed(void);
+
+/* Query min and max for addition: */
+int MC_AddMinAugend(void);              /* the "augend" is the first addend i.e. "a" in "a + b = c" */
+int MC_AddMinAddend(void);              /* options for the other addend */
+int MC_AddMaxAugend(void);
+int MC_AddMaxAddend(void);
+
+/* Query min and max for subtraction: */
+int MC_SubMinMinuend(void);             /* minuend - subtrahend = difference */
+int MC_SubMinSubtrahend(void);
+int MC_SubMaxMinuend(void);
+int MC_SubMaxSubtrahend(void);
+
+/* Query min and max for multiplication: */
+int MC_MultMinMultiplier(void);         /* multiplier * multiplicand = product */
+int MC_MultMinMultiplicand(void);
+int MC_MultMaxMultiplier(void);
+int MC_MultMaxMultiplicand(void);
+
+/* Query min and max for division: */
+int MC_DivMinDivisor(void);            /* dividend/divisor = quotient */
+int MC_DivMinQuotient(void);
+int MC_DivMaxDivisor(void);
+int MC_DivMaxQuotient(void);
+
+#endif

Modified: tuxmath/trunk/src/options.c
===================================================================
--- tuxmath/trunk/src/options.c	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/options.c	2006-05-16 17:05:20 UTC (rev 10)
@@ -22,6 +22,8 @@
 
 #include <SDL.h>
 
+#include "mathcards.h"
+
 #include "options.h"
 #include "images.h"
 #include "setup.h"
@@ -29,6 +31,7 @@
 #include "playsound.h"
 #include "game.h"
 #include "tuxmath.h"
+
 /* FIXME figure out what oper_override is supposed to do and make sure */
 /* this file behaves accordingly! */
 
@@ -55,7 +58,13 @@
   dest.y = 0;
   SDL_BlitSurface(images[IMG_OPTIONS], NULL, screen, &dest);
 
+  /* Syncrhonize old opers[] array with selections from MathCards */
+  opers[OPT_OP_ADD] = MC_AddAllowed();
+  opers[OPT_OP_SUB] = MC_SubAllowed();
+  opers[OPT_OP_MUL] = MC_MultAllowed();
+  opers[OPT_OP_DIV] = MC_DivAllowed();
 
+
   /* Draw options: */
 
   for (i = 0; i < NUM_OPTS; i++)
@@ -81,7 +90,7 @@
     {
       /* Maximum answer: */
 
-      snprintf(tmp_str, sizeof(tmp_str), "%04d", math_options->max_answer);
+      snprintf(tmp_str, sizeof(tmp_str), "%04d", MC_MaxAnswer());
       draw_nums(tmp_str,
 		screen->w - ((images[IMG_NUMS]->w / 14) * 2) - 16,
 		y + images[IMG_OPT_MAX_ANSWER]->h);
@@ -303,8 +312,34 @@
   {
     if (option >= OPT_OP_ADD && option < OPT_OP_ADD + NUM_OPERS)
     {
+      /* toggle selection of math operation - old opers array */
+      /* FIXME opers[] to go away                             */
       opers[option - OPT_OP_ADD] = !opers[option - OPT_OP_ADD];
-
+      /* toggle selection in new MathCards backend: */
+      switch (option)
+      {
+        case OPT_OP_ADD:
+        {
+          MC_SetAddAllowed(!MC_AddAllowed());
+          break;
+	}
+        case OPT_OP_SUB:
+        {
+          MC_SetSubAllowed(!MC_SubAllowed());
+          break;
+        }
+        case OPT_OP_MUL:
+        {
+          MC_SetMultAllowed(!MC_MultAllowed());
+          break;
+        }
+        case OPT_OP_DIV:
+        {
+          MC_SetDivAllowed(!MC_DivAllowed());
+          break;
+        }
+      }
+ 
       dest.x = screen->w - images[IMG_OPT_CHECK]->w - 16;
       dest.y = (images[IMG_OPTIONS]->h + 8 +
                ((option - OPT_OP_ADD) *
@@ -318,9 +353,9 @@
 
     else if (option == OPT_A_MAX)
     {
-      math_options->max_answer = (math_options->max_answer * 2) / 3;
-      if (math_options->max_answer < 12)
-        math_options->max_answer = 144;
+      MC_SetMaxAnswer((MC_MaxAnswer() * 2) / 3);
+      if (MC_MaxAnswer() < 12)
+        MC_SetMaxAnswer(144);
 
       dest.x = screen->w - ((images[IMG_NUMS]->w / 14) * 4) - 16;
       dest.y = (images[IMG_OPTIONS]->h + 8 +
@@ -330,7 +365,7 @@
       dest.h = images[IMG_OPT_MAX_ANSWER]->h;
 
       SDL_FillRect(screen, &dest, SDL_MapRGB(screen->format, 0, 0, 0));
-      snprintf(tmp_str, sizeof(tmp_str), "%04d", math_options->max_answer);
+      snprintf(tmp_str, sizeof(tmp_str), "%04d", MC_MaxAnswer());
       draw_nums(tmp_str,
                 screen->w - ((images[IMG_NUMS]->w / 14) * 2) - 16,
                 (images[IMG_OPTIONS]->h + 8 +
@@ -365,6 +400,7 @@
 
     else if (option == OPT_Q_RANGE)
     {
+    /* FIXME question ranges now handled in MathCards */
     /* Change which ranges are available: */
       range_bits = range_bits + 1;
       if (range_bits >= (1 << NUM_Q_RANGES))
@@ -399,7 +435,36 @@
             + images[IMG_OPT_RNG_1_5 + j * 2 + range_enabled[j]]-> w 
             + 16;
       }
+      
+      /* update settings in MathCards: */
+      {
+        int lowest_range, highest_range, minimum, maximum;
+        lowest_range = NUM_Q_RANGES - 1;
+        highest_range = 0;
+
+        /* find lowest and highest enabled ranges */
+        for (j = 0; j < NUM_Q_RANGES; j++)
+        {
+          if (range_enabled[j] && j < lowest_range)
+            lowest_range = j;
+          if (range_enabled[j] && j > highest_range)
+            highest_range = j;
+        } 
+        minimum = ranges[lowest_range].min;
+        maximum = ranges[highest_range].max;
+
+        /* update MathCards: */
+        MC_SetAddMin(minimum);
+        MC_SetAddMax(maximum);
+        MC_SetSubMin(minimum);
+        MC_SetSubMax(maximum);
+        MC_SetMultMin(minimum);
+        MC_SetMultMax(maximum);
+        MC_SetDivMin(minimum);
+        MC_SetDivMax(maximum);
+      }
     }
+ 
     /* same sound for all option updates */
     playsound(SND_LASER);
   }

Modified: tuxmath/trunk/src/setup.c
===================================================================
--- tuxmath/trunk/src/setup.c	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/setup.c	2006-05-16 17:05:20 UTC (rev 10)
@@ -29,6 +29,8 @@
 #endif
 #include <SDL_image.h>
 
+#include "mathcards.h"
+
 #include "tuxmath.h"
 #include "setup.h"
 #include "images.h"
@@ -164,16 +166,14 @@
 
 int opers[NUM_OPERS], range_enabled[NUM_Q_RANGES];
 
-math_option_type* math_options;
 game_option_type* game_options;
 
 /* Local function prototypes: */
 
 void seticon(void);
 void usage(int err, char * cmd);
-int initialize_math_options(math_option_type* opts);
+
 int initialize_game_options(game_option_type* opts);
-void print_math_options(math_option_type* opts);
 void print_game_options(game_option_type* opts);
 
 /* --- Set-up function! --- */
@@ -185,31 +185,29 @@
 
   screen = NULL;
 
-  /* initialize game_options and math_options structs with defaults DSB */
-  /* FIXME: Program should load options from disk */
-  math_options = malloc(sizeof(math_option_type));
-  if (!initialize_math_options(math_options))
+  if (!MC_Initialize())
   {
-    printf("\nUnable to initialize math_options");
-    fprintf(stderr, "\nUnable to initialize math_options");
+    printf("\nUnable to initialize MathCards\n");
+    fprintf(stderr, "\nUnable to initialize MathCards\n");
     exit(1);
   }
 
+  /* initialize game_options struct with defaults DSB */
   game_options = malloc(sizeof(game_option_type));
   if (!initialize_game_options(game_options))
   {
-    printf("\nUnable to initialize game_options");
-    fprintf(stderr, "\nUnable to initialize game_options");
+    printf("\nUnable to initialize game_options\n");
+    fprintf(stderr, "\nUnable to initialize game_options\n");
     exit(1);
   }
 
-
-
+/* FIXME will not need this when MathCards used */ 
+/* 
   for (i = 0; i < NUM_OPERS; i++)
   {
     opers[i] = 1;
   }
-
+*/
   for (i = 0; i < NUM_Q_RANGES; i++)
   { 
     range_enabled[i] = 1;
@@ -339,7 +337,7 @@
     else if (strcmp(argv[i], "--allownegatives") == 0 ||
              strcmp(argv[i], "-n") == 0)
     {
-      math_options->allow_neg_answer = 1;
+      MC_SetAllowNegAnswer(1);
     }
     else if (strcmp(argv[i], "--speed") == 0 ||
 	     strcmp(argv[i], "-s") == 0)
@@ -615,100 +613,18 @@
 /* free any heap memory used during game DSB */
 void cleanup()
 {
-  if (math_options)
-    free(math_options);
   if (game_options)
     free(game_options);
+  MC_EndGame();
 }
 
 /* Set up math_options struct with defaults from tuxmath.h, */
 /* with simple sanity check for negatives                   */
 /* FIXME Should there be more error checking here?          */
+/* FIXME move this to mathcards.c                           */
 
-int initialize_math_options(math_option_type* opts)
-{
-  /* bail out if no struct */
-  if (!opts)
-    return 0;
 
-  /* set general math options */
-  opts->allow_neg_answer = DEFAULT_ALLOW_NEG_ANSWER;
-  opts->max_answer = DEFAULT_MAX_ANSWER;
-  opts->max_questions = DEFAULT_MAX_QUESTIONS;
-  opts->format_answer_last = DEFAULT_FORMAT_ANSWER_LAST;
-  opts->format_answer_first = DEFAULT_FORMAT_ANSWER_FIRST;
-  opts->format_answer_middle = DEFAULT_FORMAT_ANSWER_MIDDLE;
-  opts->question_copies = DEFAULT_QUESTION_COPIES;
-  /* set addition options */
-  opts->addition_allowed = DEFAULT_ADDITION_ALLOWED;
-  opts->min_augend = DEFAULT_MIN_AUGEND;
-  opts->max_augend = DEFAULT_MAX_AUGEND;
-  opts->min_addend = DEFAULT_MIN_ADDEND;
-  opts->max_addend = DEFAULT_MAX_ADDEND;
-  /* set subtraction options */
-  opts->subtraction_allowed = DEFAULT_SUBTRACTION_ALLOWED;
-  opts->min_minuend = DEFAULT_MIN_MINUEND;
-  opts->max_minuend = DEFAULT_MAX_MINUEND;
-  opts->min_subtrahend = DEFAULT_MIN_SUBTRAHEND;
-  opts->max_subtrahend = DEFAULT_MAX_SUBTRAHEND;
-  /* set multiplication options */
-  opts->multiplication_allowed = DEFAULT_MULTIPLICATION_ALLOWED;
-  opts->min_multiplier = DEFAULT_MIN_MULTIPLIER;
-  opts->max_multiplier = DEFAULT_MAX_MULTIPLIER;
-  opts->min_multiplicand = DEFAULT_MIN_MULTIPLICAND;
-  opts->max_multiplicand = DEFAULT_MAX_MULTIPLICAND;
-  /* set division options */
-  opts->division_allowed = DEFAULT_DIVISION_ALLOWED;
-  opts->min_divisor = DEFAULT_MIN_DIVISOR;
-  opts->max_divisor = DEFAULT_MAX_DIVISOR;
-  opts->min_quotient = DEFAULT_MIN_QUOTIENT;
-  opts->max_quotient = DEFAULT_MAX_QUOTIENT;
 
-  /* if no negatives to be used, reset any negatives to 0 */
-  if (!opts->allow_neg_answer)
-  {
-    if (opts->min_augend < 0)
-      opts->min_augend = 0;
-    if (opts->max_augend < 0)
-      opts->max_augend = 0;
-    if (opts->min_addend < 0)
-      opts->min_addend = 0;
-    if (opts->max_addend < 0)
-      opts->max_addend = 0;
-
-    if (opts->min_minuend < 0)
-      opts->min_minuend = 0;
-    if (opts->max_minuend < 0)
-      opts->max_minuend = 0;
-    if (opts->min_subtrahend < 0)
-      opts->min_subtrahend = 0;
-    if (opts->max_subtrahend < 0)
-      opts->max_subtrahend = 0;
-
-    if (opts->min_multiplier < 0)
-      opts->min_multiplier = 0;
-    if (opts->max_multiplier < 0)
-      opts->max_multiplier = 0;
-    if (opts->min_multiplicand < 0)
-      opts->min_multiplicand = 0;
-    if (opts->max_multiplicand < 0)
-      opts->max_multiplicand = 0;
-
-    if (opts->min_divisor < 0)
-      opts->min_divisor = 0;
-    if (opts->max_divisor < 0)
-      opts->max_divisor = 0;
-    if (opts->min_quotient < 0)
-      opts->min_quotient = 0;
-    if (opts->max_quotient < 0)
-      opts->max_quotient = 0;
-  }
-  
-  /* for testing purposes */
-  /* print_math_options(opts); */ 
-  return 1;
-}
-
 int initialize_game_options(game_option_type* opts)
 {
   /* bail out if no struct */
@@ -724,7 +640,11 @@
   opts->use_keypad = DEFAULT_USE_KEYPAD;
   opts->speed = DEFAULT_SPEED;
   opts->allow_speedup = DEFAULT_ALLOW_SPEEDUP;
+  opts->speedup_factor = DEFAULT_SPEEDUP_FACTOR;
+  opts->max_speed = DEFAULT_MAX_SPEED;
+  opts->slow_after_wrong = DEFAULT_SLOW_AFTER_WRONG;
   opts->reuse_questions = DEFAULT_REUSE_QUESTIONS;
+  opts->extra_comets = DEFAULT_EXTRA_COMETS;
   opts->max_comets = DEFAULT_MAX_COMETS;
   opts->num_cities = DEFAULT_NUM_CITIES;   /* MUST BE AN EVEN NUMBER! */
   opts->num_bkgds = DEFAULT_NUM_BKGDS;
@@ -735,49 +655,8 @@
   return 1;
 }
 
-/* prints struct to stdout for testing purposes */
-void print_math_options(math_option_type* opts)
-{
- /* bail out if no struct */
-  if (!opts)
-    return;
 
-  printf("\nPrinting members of math_options struct:\n");
-  printf("\nGeneral math options:\n");
-  printf("allow_neg_answer:\t%d\n", opts->allow_neg_answer);
-  printf("max_answer:\t%d\n", opts->max_answer);
-  printf("max_questions:\t%d\n", opts->max_questions);
-  printf("format_answer_last:\t%d\n", opts->format_answer_last);
-  printf("format_answer_first:\t%d\n", opts->format_answer_first);
-  printf("format_answer_middle:\t%d\n", opts->format_answer_middle);
-  printf("question_copies:\t%d\n", opts->question_copies);
 
-  printf("\nSpecific math operation options:\n");
-  printf("addition_allowed:\t%d\n", opts->addition_allowed);
-  printf("min_augend:\t%d\n", opts->min_augend);
-  printf("max_augend:\t%d\n", opts->max_augend);
-  printf("min_addend:\t%d\n", opts->min_addend);
-  printf("max_addend:\t%d\n", opts->max_addend);
-
-  printf("subtraction_allowed\t%d\n", opts->subtraction_allowed);
-  printf("min_minuend:\t%d\n", opts->min_minuend);
-  printf("max_minuend:\t%d\n", opts->max_minuend);
-  printf("min_subtrahend:\t%d\n", opts->min_subtrahend);
-  printf("max_subtrahend:\t%d\n", opts->max_subtrahend);
-
-  printf("multiplication_allowed:\t%d\n", opts->multiplication_allowed);
-  printf("min_multiplier:\t%d\n", opts->min_multiplier);
-  printf("max_multiplier:\t%d\n", opts->max_multiplier);
-  printf("min_multiplicand:\t%d\n", opts->min_multiplicand);
-  printf("max_multiplicand:\t%d\n", opts->max_multiplicand);
-
-  printf("division_allowed:\t%d\n", opts->division_allowed);
-  printf("min_divisor:\t%d\n",opts->min_divisor);
-  printf("max_divisor:\t%d\n", opts->max_divisor);
-  printf("min_quotient:\t%d\n", opts->min_quotient);
-  printf("max_quotient:\t%d\n", opts->max_quotient);
-}
-
 /* prints struct to stdout for testing purposes */
 void print_game_options(game_option_type* opts)
 {
@@ -795,6 +674,15 @@
   printf("use_keypad:\t%d\n", opts->use_keypad);
   printf("reuse_questions:\t%d\n", opts->reuse_questions);
   printf("speed:\t%f\n", opts->speed);
+  printf("allow_speedup:\t%d\n", opts->allow_speedup);
+  printf("speedup_factor:\t%f\n", opts->speedup_factor);
+  printf("max_speed:\t%f\n", opts->max_speed);
+  printf("slow_after_wrong:\t%d\n", opts->slow_after_wrong);
+  printf("extra_comets:\t%d\n", opts->extra_comets);
+  printf("max_comets:\t%d\n", opts->max_comets);
+  printf("num_cities:\t%d\n", opts->num_cities);
+  printf("num_bkgds:\t%d\n", opts->num_bkgds);
+  printf("max_city_colors:\t%d\n", opts->max_city_colors);
 }
 
 /* Set the application's icon: */

Modified: tuxmath/trunk/src/tuxmath.c
===================================================================
--- tuxmath/trunk/src/tuxmath.c	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/tuxmath.c	2006-05-16 17:05:20 UTC (rev 10)
@@ -27,7 +27,7 @@
 #include "tuxmath.h"
 
 /* global data: */
-math_option_type* math_options; /* used by setup.c, options.c, game.c */
+/*MC_Options* math_options;  used by setup.c, options.c, game.c */
 game_option_type* game_options; /* used by setup.c, options.c, game.c */
 
 int main(int argc, char * argv[])

Modified: tuxmath/trunk/src/tuxmath.h
===================================================================
--- tuxmath/trunk/src/tuxmath.h	2006-03-08 13:36:25 UTC (rev 9)
+++ tuxmath/trunk/src/tuxmath.h	2006-05-16 17:05:20 UTC (rev 10)
@@ -20,44 +20,9 @@
 #ifndef TUXMATH_H
 #define TUXMATH_H
 
-/* This struct contains all options that determine what */
-/* math questions are asked during a game */
-typedef struct math_option_type {
-  /* general math options */
-  int allow_neg_answer;
-  int max_answer;
-  int max_questions;
-  int format_answer_last;
-  int format_answer_first;
-  int format_answer_middle;
-  int question_copies;
-  /* addition options */
-  int addition_allowed;
-  int min_augend;  /* the "augend" is the first addend i.e. "a" in "a + b = c" */
-  int max_augend;
-  int min_addend;  /* options for the other addend */
-  int max_addend;
-  /* subtraction options */
-  int subtraction_allowed;
-  int min_minuend;  /* minuend - subtrahend = difference */
-  int max_minuend;
-  int min_subtrahend;
-  int max_subtrahend;
-  /* multiplication options */
-  int multiplication_allowed;
-  int min_multiplier;  /* multiplier * multiplicand = product */
-  int max_multiplier;
-  int min_multiplicand;
-  int max_multiplicand;
-  /* division options */
-  int division_allowed;
-  int min_divisor;    /* dividend/divosor = quotient */
-  int max_divisor;
-  int min_quotient;
-  int max_quotient;
-} math_option_type;
+#undef TUXMATH_DEBUG   /* for conditional compilation of debugging output */
 
-/* this struct contains all other options regarding general */
+/* this struct contains all options regarding general       */
 /* gameplay but not having to do with math questions per se */
 typedef struct game_option_type {
   /* general game options */
@@ -70,63 +35,40 @@
   int reuse_questions;
   float speed;
   int allow_speedup;
-  int max_comets;
+  float speedup_factor;
+  float max_speed;
+  int slow_after_wrong;
+  int extra_comets;
+  int max_comets;  /*FIXME not being used */
   /* not sure the rest of these belong in here */
   int num_cities;  /* MUST BE AN EVEN NUMBER! */
   int num_bkgds;
   int max_city_colors;
 } game_option_type;
 
+
 /* make option data accessible to rest of program */
-extern math_option_type* math_options; /* used by setup.c, options.c, game.c */
 extern game_option_type* game_options; /* used by setup.c, options.c, game.c */
 
-/* default values for math_options */
-#define DEFAULT_ALLOW_NEG_ANSWER 0
-#define DEFAULT_MAX_ANSWER 144
-#define DEFAULT_MAX_QUESTIONS 5000
-#define DEFAULT_FORMAT_ANSWER_LAST 1      /* question format is: a + b = ? */
-#define DEFAULT_FORMAT_ANSWER_FIRST 0     /* question format is: ? + b = c */
-#define DEFAULT_FORMAT_ANSWER_MIDDLE 0    /* question format is: a + ? = c */
-#define DEFAULT_QUESTION_COPIES 1         /* how many times each question is put in list */
-
-#define DEFAULT_ADDITION_ALLOWED 1
-#define DEFAULT_MIN_AUGEND 0              /* the "augend" is the first addend i.e. "a" in "a + b = c" */
-#define DEFAULT_MAX_AUGEND 12
-#define DEFAULT_MIN_ADDEND 0
-#define DEFAULT_MAX_ADDEND 12
-
-#define DEFAULT_SUBTRACTION_ALLOWED 1     /* minuend - subtrahend = difference */
-#define DEFAULT_MIN_MINUEND 0
-#define DEFAULT_MAX_MINUEND 24
-#define DEFAULT_MIN_SUBTRAHEND 0
-#define DEFAULT_MAX_SUBTRAHEND 12
-
-#define DEFAULT_MULTIPLICATION_ALLOWED 1
-#define DEFAULT_MIN_MULTIPLIER 0          /* multiplier * multiplicand = product */
-#define DEFAULT_MAX_MULTIPLIER 12
-#define DEFAULT_MIN_MULTIPLICAND 0
-#define DEFAULT_MAX_MULTIPLICAND 6
-
-#define DEFAULT_DIVISION_ALLOWED 0        /* dividend/divisor = quotient */
-#define DEFAULT_MIN_DIVISOR 0             /* note - generate_list() will prevent */
-#define DEFAULT_MAX_DIVISOR 3             /* questions with division by zero.    */
-#define DEFAULT_MIN_QUOTIENT 0
-#define DEFAULT_MAX_QUOTIENT 3
-
 /* default values for game_options */
 #define DEFAULT_USE_SOUND 1
 #define DEFAULT_FULLSCREEN 0
 #define DEFAULT_USE_BKGD 1
 #define DEFAULT_DEMO_MODE 0
 #define DEFAULT_OPER_OVERRIDE 0
-#define DEFAULT_USE_KEYPAD 1
+#define DEFAULT_USE_KEYPAD 0
 #define DEFAULT_REUSE_QUESTIONS 0
 #define DEFAULT_SPEED 1
-#define DEFAULT_ALLOW_SPEEDUP 0
-#define DEFAULT_MAX_COMETS 2	/* CHANGED FROM 10 BY DSB */
+#define DEFAULT_ALLOW_SPEEDUP 1
+#define DEFAULT_SPEEDUP_FACTOR 1
+#define DEFAULT_MAX_SPEED 10
+#define DEFAULT_SLOW_AFTER_WRONG 0
+#define DEFAULT_EXTRA_COMETS 2
+#define DEFAULT_MAX_COMETS 4	
 #define DEFAULT_NUM_CITIES 4   /* MUST BE AN EVEN NUMBER! */
 #define DEFAULT_NUM_BKGDS 5
 #define DEFAULT_MAX_CITY_COLORS 4
 
+/* NOTE: default values for math options are now in mathcards.h */
+
 #endif




More information about the Tux4kids-commits mailing list