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

dbruce at alioth.debian.org dbruce at alioth.debian.org
Thu Mar 8 21:03:35 CET 2007


Author: dbruce
Date: 2006-08-11 16:27:38 +0000 (Fri, 11 Aug 2006)
New Revision: 20

Added:
   tuxmath/trunk/data/.tuxmath
   tuxmath/trunk/src/config.c
   tuxmath/trunk/src/config.h
Modified:
   tuxmath/trunk/Makefile
   tuxmath/trunk/docs/README.txt
   tuxmath/trunk/docs/TODO.txt
   tuxmath/trunk/docs/changelog
   tuxmath/trunk/src/game.c
   tuxmath/trunk/src/mathcards.c
   tuxmath/trunk/src/mathcards.h
   tuxmath/trunk/src/setup.c
   tuxmath/trunk/src/setup.h
   tuxmath/trunk/src/title.c
   tuxmath/trunk/src/tuxmath.h
Log:
Supports reading and writing config file


Modified: tuxmath/trunk/Makefile
===================================================================
--- tuxmath/trunk/Makefile	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/Makefile	2006-08-11 16:27:38 UTC (rev 20)
@@ -67,7 +67,7 @@
 
 tuxmath:	obj/tuxmath.o obj/setup.o obj/title.o obj/game.o \
 		obj/options.o obj/credits.o obj/playsound.o \
-                obj/mathcards.o
+                obj/mathcards.o obj/config.o
 	@echo "LINKING!"
 	$(CC) $(CFLAGS) $^ -o tuxmath $(LIBS)
 
@@ -77,7 +77,7 @@
 
 obj/tuxmath.o:	src/tuxmath.c src/images.h src/sounds.h src/setup.h \
 		src/title.h src/game.h src/options.h src/credits.h \
-		src/playsound.h
+		src/playsound.h 
 	@echo "BUILDING tuxmath.o"
 	-mkdir -p obj
 	$(CC) $(CFLAGS) src/tuxmath.c -c -o obj/tuxmath.o
@@ -121,3 +121,8 @@
 	@echo "BUILDING mathcards.o"
 	-mkdir -p obj
 	$(CC) $(CFLAGS) src/mathcards.c -c -o obj/mathcards.o
+
+obj/config.o:	src/config.c src/config.h 
+	@echo "BUILDING config.o"
+	-mkdir -p obj
+	$(CC) $(CFLAGS) src/config.c -c -o obj/config.o

Added: tuxmath/trunk/data/.tuxmath
===================================================================
--- tuxmath/trunk/data/.tuxmath	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/data/.tuxmath	2006-08-11 16:27:38 UTC (rev 20)
@@ -0,0 +1,216 @@
+############################################################
+#                                                          #
+#                 Tuxmath Config File                      #
+#                                                          #
+############################################################
+
+
+############################################################
+#                                                          #
+#                 General Game Options                     #
+#                                                          #
+# The following options are boolean (true/false) variables #
+# that control various aspects of Tuxmath's behavior.      #
+# The program writes the values to the file as either '0'  #
+# or '1'. However, the program accepts 'n', 'no', 'f', and #
+# 'false' as synonyms for '0', and similarly accepts 'y',  #
+# 'yes', 't', and 'true' as synonyms for '1' (all case-    #
+# insensitive).                                            #
+############################################################
+
+############################################################
+# 'per_user_config' determines whether Tuxmath will look   #
+# in the user's home directory for settings. Default is 1  #
+# (yes). If deselected, the program will ignore the user's #
+# .tuxmath file and use the the global settings in the     #
+# installation-wide config file.                           #
+# This setting cannot be changed by an ordinary user.      #
+############################################################
+per_user_config = 1
+
+# Self-explanatory, default is 1:
+use_sound = 1
+
+# Use either fullscreen at 640x480 resolution or window of that size
+# Default is 1.  Change to 0 if SDL has trouble with fullscreen.
+fullscreen = 1
+
+# Use 640x480 jpg image for background; default is 1.
+use_bkgd = 1
+
+# Program runs as demo; default is 0.
+demo_mode = 0
+
+# Use operator selection from command line; default is 0.
+oper_override = 0
+
+# Display onscreen numeric keypad; default is 0.
+use_keypad = 0
+
+############################################################
+# The remaining settings determine the speed and number    #
+# of comets.  The speed settings are float numbers (mean-  #
+# ing decimals allowed). The comet settings are integers.  #
+############################################################
+
+# Starting comet speed. Default is 1.
+speed = 1.000000
+
+# Speed is multiplied by this factor with each new wave.
+# Default is 1.2
+speedup_factor = 1.200000
+
+# Maximum speed. Default is 10.
+max_speed = 10.000000
+
+# Number of comets for first wave. Default is 2.
+starting_comets = 2
+
+# Comets to add for each successive wave. Default is 2.
+extra_comets_per_wave = 2
+
+# Maximum number of comets. Default is 10.
+max_comets = 10
+
+# Allow speed and number of comets to increase with each
+# wave.  May want to turn this off for smaller kids. Default is 1.
+allow_speedup = 1
+
+# Go back to starting speed and number of comets if player
+# misses a question. Useful for smaller kids. Default is 0.
+slow_after_wrong = 0
+
+############################################################
+#                                                          #
+#                  General Math Options                    #
+#                                                          #
+# If 'play_through_list' selected, Tuxmath will ask each   #
+# question in the defined list. The game ends when no      #
+# questions remain.  Default is 1 (i.e. 'true' or 'yes').  #
+#                                                          #
+# 'question_copies' is the number of times each question   #
+# will be asked. It can be 1 to 10 - Default is 1.         #
+#                                                          #
+# 'repeat_wrongs' tells Tuxmath whether to reinsert        #
+# incorrectly answered questions into the list to be       #
+# asked again. Default is 1 (yes).                         #
+#                                                          #
+# 'copies_repeated_wrongs' gives the number of times an    #
+# incorrectly answered question will reappear. Default     #
+# is 1.                                                    #
+#                                                          #
+# The defaults for these values result in a 'mission'      #
+# for Tux that is accomplished by answering all            #
+# questions correctly with at least one surviving city.    #
+############################################################
+
+play_through_list = 1
+question_copies = 1
+repeat_wrongs = 1
+copies_repeated_wrongs = 1
+
+############################################################
+# 'format_answer_last' (, _first, middle) control the      #
+# generation of questions with the answer in different     #
+# places in the equation.  i.e.:                           #
+#                                                          #
+#    format_answer_last:    2 + 2 = ?                      #
+#    format_answer_first:   ? + 2 = 4                      #
+#    format_answer_middle:  2 + ? = 4                      #
+#                                                          #
+# By default, 'format_answer_first' is enabled and the     #
+# other two formats are disabled.  Note that the options   #
+# are not mutually exclusive - the question list may       #
+# contain questions with different formats.                #
+############################################################
+
+format_answer_last = 1
+format_answer_first = 0
+format_answer_middle = 0
+
+############################################################
+# 'allow_negatives' allows or disallows use of negative    #
+# numbers as both operands and answers.  Default is 0      #
+# (no), which disallows questions like:                    #
+#          2 - 4 = ?                                       #
+# Note: this option must be enabled in order to set the    #
+# operand ranges to include negatives (see below). If it   #
+# is changed from 1 (yes) to 0 (no), any negative          #
+# operand limits will be reset to 0.                       #
+############################################################
+
+allow_negatives = 0
+
+############################################################
+# 'max_answer' is the largest absolute value allowed in    #
+# any value in a question (not only the answer). Default   #
+# is 144. It can be set as high as 999.                    #
+############################################################
+
+max_answer = 144
+
+############################################################
+# 'max_questions' is limit of the length of the question   #
+# list. Default is 5000 - only severe taskmasters will     #
+# need to raise it.                                        #
+############################################################
+
+max_questions = 5000
+
+############################################################
+# If 'randomize' selected, the list will be shuffled       #
+# at the start of the game.  Default is 1 (yes).           #
+############################################################
+
+randomize = 1
+
+############################################################
+#                                                          #
+#                 Math Operations Allowed                  #
+#                                                          #
+# These options enable questions for each of the four math #
+# operations.  All are 1 (yes) by default.                 #
+############################################################
+
+addition_allowed = 1
+subtraction_allowed = 1
+multiplication_allowed = 1
+division_allowed = 1
+
+############################################################
+#                                                          #
+#      Minimum and Maximum Values for Operand Ranges       #
+#                                                          #
+# Operand limits can be set to any integer up to the       #
+# value of 'max_answer'.  If 'allow_negatives' is set to 1 #
+# (yes), either negative or positive values can be used.   #
+# Tuxmath will generate questions for every value in the   #
+# specified range. The maximum must be greater than or     #
+# equal to the corresponding minimum for any questions to  #
+# be generated for that operation.                         #
+############################################################
+
+
+# Addition operands: augend + addend = sum
+min_augend = 0
+max_augend = 12
+min_addend = 0
+max_addend = 12
+
+# Subtraction operands: minuend - subtrahend = difference
+min_minuend = 0
+max_minuend = 12
+min_subtrahend = 0
+max_subtrahend = 12
+
+# Multiplication operands: multiplier * multiplicand = product
+min_multiplier = 0
+max_multiplier = 12
+min_multiplicand = 0
+max_multiplicand = 12
+
+# Division operands: dividend/divisor = quotient
+min_divisor = 0
+max_divisor = 12
+min_quotient = 0
+max_quotient = 12

Modified: tuxmath/trunk/docs/README.txt
===================================================================
--- tuxmath/trunk/docs/README.txt	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/docs/README.txt	2006-08-11 16:27:38 UTC (rev 20)
@@ -2,9 +2,9 @@
 An educational math tutorial game starring Tux, the Linux Penguin
 -----------------------------------------------------------------
 
-June 17, 2006
+August 11, 2006
 
-For tuxmath-0.8
+For tuxmath-0.9
 
 Objective
 ---------
@@ -49,9 +49,12 @@
 
   Command Line Options
   --------------------
+    NOTE: editing the config file is now a much better way to control the 
+    behavior of Tuxmath - SEE BELOW.
+
     The following command-line options can be sent to the program.
 
-     --norepeats      -  Game consists of working through a list of questions
+     --playthroughlist - Game consists of working through a list of questions
       -r                 generated based on the selected options (or defaults).
                          If a comet strikes a city without being shot down by
                          the player, the question is reinserted into the list
@@ -219,19 +222,20 @@
 
   Ending The Game
   ---------------
-    By default, the game operates in an arcade-style manner, continuing
-    until you lose all of your cities.  A GAME OVER screen is displayed.
-    By pressing any key or clicking the mouse, you return to the title
-    screen.
 
-    It is now possible to play through a defined list of questions. This
-    mode is selected via the "--norepeats" command line argument. By
-    default, the questions are asked in a random order.  If answered
+    The default mode is now to play through a defined list of questions. This
+    mode is selected by setting the config file 'play_through_list' parameter
+    to 1 ('yes' or 'true'), or via the "--playthroughlist" command line argument. 
+    By default, the questions are asked in a random order.  If answered
     correctly, they are removed.  A question that is not answered correctly
     (allowing the comet to destroy its target) will reappear in random
     order.  If all questions are successfully answered before the cities
     have been destroyed, the player wins and a "victory" screen is displayed.
 
+    The older arcade-style mode is also supported, in which the game continues
+    until you lose all of your cities.  A GAME OVER screen is displayed.
+    By pressing any key or clicking the mouse, you return to the title
+    screen.
 
   Regaining Cities
   ----------------
@@ -246,46 +250,44 @@
 Setting Game Options
 --------------------
   [ UNDER CONSTRUCTION ]
-  This is still under construction, but many things can be set.  For now,
-  there are three ways to set game options.
 
-  1. Many command-line options are supported (see above).
+  1. The program now reads and writes the settings to disk in a human-readable
+  fashion, where they can be modified with a text editor. The file is created
+  in the user's home directory and is called ".tuxmath".  The file contains
+  extensive comments describing all settings. By editing and saving this file,
+  very extensive control over the program is supported, particularly with
+  respect to generation of math questions. There really is no need to use
+  command-line options any more. In the near future, I plan to
+  include a series of "lessons" that could be played in a planned order.
 
-  2. The "Options" screen allows several parameters to be set at run-time,
+  2. Many command-line options are supported (see above). 
+
+  3. The "Options" screen allows several parameters to be set at run-time,
   or reset between individual games while the program is still running.
   Currently supported settings include the math operations to be used for
   questions, the starting speed, the maximum value of answers (for division
   questions, this is the maximum size of the dividend, not actually the 
-  answer), and ranges of numbers to be used to generate questions.
+  answer), and ranges of numbers to be used to generate questions. However,
+  many settings are only selectable via the config file.  This will be addressed
+  in a later version of the program.
 
   3. Editing the default values in tuxmath.h and mathcards.h and recompiling.
-  The default settings for general game options are contained in tuxmath.h, and
-  the defaults for math question settings are in mathcards.h.  Very fine-grained
-  control over game behavior is offered, but this isn't exactly a user-friendly
-  method of controlling the program.
+  Now that the program reads and writes the settings from/to disc, this is of
+  interest only to developers.
 
-  Two main improvements are planned.  First, the program should read
-  and write the settings to disk in a human-readable fashion, where they
-  could be modified with a text editor.  This also would allow creation of
-  a series of "lessons" that could be played in a planned order.
-  Second, the "Options" screen needs to be overhauled to give access to all
-  settings from within the program.
 
-
 Setting Administrative Options
 ------------------------------
   "Tux, of Math Command" allows parents/teachers to adjust which parts
   of the game options can be changed by the player.
 
-  For example, if you wish to, you can completely lock out all
-  "Division" questions.  The students/children will still be able to enable
-  and disable "Addition," "Subtraction," and "Multiplication" as they
-  wish.
+  The game options are first read from a master config file in the program's
+  data directory (/usr/local/share/tuxmath/.tuxmath on *nix if installed 
+  using "make install"), then overridden by the user's own .tuxmath config
+  file if "per_user_config" is selected in the master .tuxmath config file.
+  If "per_user_config" is deselected, the game starts up with the master
+  settings.  (This is somewhat under construction).
 
-  On the other hand, you may wish to lock-in the other three kinds
-  of equations, so that the players cannot disable any of them.
-  All games will always have addition, subtraction and multiplication
-  problems, but will never have division problems.
 
   [ UNDER CONSTRUCTION ]
 
@@ -303,6 +305,8 @@
   Designed by Sam "Criswell" Hart  <criswell at geekcomix.com>
   Software by Bill Kendrick  <bill at newbreedsoftware.com>
 
+  Current maintainer/programmer David Bruce <dbruce at tampabay.rr.com>
+
   Please see the game's "Credits" screen for a complete list of contributors.
 
 

Modified: tuxmath/trunk/docs/TODO.txt
===================================================================
--- tuxmath/trunk/docs/TODO.txt	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/docs/TODO.txt	2006-08-11 16:27:38 UTC (rev 20)
@@ -1,7 +1,10 @@
 TODO.txt for "tuxmath"
 
-June 17, 2006
+August 11, 2006
 
+Build:
+  * Fix certain file permissions issues when packaged as rpm.
+  * Consider using cmake.
 
 Graphics:
   * Consider changing city graphics to something less violent.
@@ -30,8 +33,6 @@
 Options:
   * Make all of settings for MathCards backend settable at run time rather than just
     at compile time - will require major overall of Options screen(s).
-  * Have program read and write settings to disk as a human-readable text file.  Once
-    this is done, games could be played as a planned series of "lessons".
   * Display numbers instead of formulas if no operators are selected: "typing tutor" mode.
   * Save options.
   * Admin options screen / files

Modified: tuxmath/trunk/docs/changelog
===================================================================
--- tuxmath/trunk/docs/changelog	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/docs/changelog	2006-08-11 16:27:38 UTC (rev 20)
@@ -1,5 +1,35 @@
-CHANGES.txt for "tuxmath"
+changelog for "tuxmath"
 
+2006.Aug.11 (https://svn.tux4kids.net/tuxmath/ - revision 18)
+  Version 0.9 
+
+  Setup:
+    * Tuxmath now reads and writes all settings to disc in human-
+      readable form. The first time it is run by each user, it
+      creates a config file (called .tuxmath for now) in the user's
+      home directory  The settings are initially set to hard-coded
+      defaults, then overridden as needed by a master config file
+      (for now located in DATA_PREFIX i.e. /usr/local/share/tuxmath),
+      then overridden if desired by the user's own config file.
+      The settings are saved from game to game if the file is left
+      unchanged.  Any text editor can be used to modify the settings.
+      The config file contains extensive comments documenting
+      all settings.  Complete control over program behavior is now
+      available without recompiling.
+    * tuxmath -h output updated with prominent notice that config
+      file now being used.
+  Game:
+    * By default the game now plays through the question list and 
+      ends with "Victory" when all questions correctly answered.
+    * game.c now handles 'slow_after_wrong' and other comet and speed
+      settings correctly.
+    * title screen has shortcuts 'P' for Play, 'O' for Options, etc.
+  Code:
+    * names of some of settings in game_options and math_options
+      structs changed to more intuitive/descriptive ones.
+  Docs:
+    * Updates to README.txt and TODO.txt
+
 2006.Jun.17 (https://svn.tux4kids.net/tuxmath/ - revision 10)
   Docs:
     * Updates to README.txt and TODO.txt.

Added: tuxmath/trunk/src/config.c
===================================================================
--- tuxmath/trunk/src/config.c	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/config.c	2006-08-11 16:27:38 UTC (rev 20)
@@ -0,0 +1,572 @@
+/*
+*  C Implementation: config.c
+*
+* (Note: this code was made possible by studying the file prefs.c in gtkpod:
+*  URL: http://www.gtkpod.org/
+*  URL: http://gtkpod.sourceforge.net/
+*  Copyright (C) 2002-2005 Jorg Schuler <jcsjcs at users sourceforge net>.
+*  Licensed under GNU GPL v2.
+*  This code is a nearly complete rewrite but I would like to express my thanks.)
+*
+*
+* Description: This file contains functions to read and write config files.
+* The config file contains name-value pairs, one pair per line, to control
+* settings for the behavior of Tuxmath.
+*
+
+* 
+* Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
+*
+* Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL)
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "tuxmath.h"
+#include "setup.h"
+#include "mathcards.h"
+
+static int str_to_bool(char* val);
+
+int read_config_file(FILE *fp, int file_type)
+{
+  char buf[PATH_MAX];
+  char *parameter, *param_begin, *param_end, *value, *value_end;
+
+  #ifdef TUXMATH_DEBUG
+  printf("\nEntering read_config_file()\n");
+  #endif
+
+  /* get out if file pointer invalid: */
+  if(!fp)
+  {
+    #ifdef TUXMATH_DEBUG
+    printf("config file pointer invalid!\n");
+    printf("Leaving read_config_file()\n");
+    #endif
+
+    fprintf(stderr, "config file pointer invalid!\n");
+    return 0;
+  }
+
+  /* make sure we start at beginning: */
+  rewind(fp);
+
+  /* read in a line at a time: */
+  while (fgets (buf, PATH_MAX, fp))
+  { 
+    #ifdef TUXMATH_DEBUG
+    printf("Beginning fgets() loop\n");
+    #endif
+    /* "parameter" and "value" will contain the non-whitespace chars */
+    /* before and after the '=' sign, respectively.  e.g.:           */
+    /*                                                               */
+    /* fullscreen = 0;                                               */
+    /* parameter is "fullscreen"                                     */
+    /* value is '0'                                                  */
+    /*                                                               */
+
+    /* ignore comment lines */
+    if ((buf[0] == ';') || (buf[0] == '#'))
+    {
+      #ifdef TUXMATH_DEBUG
+      printf("Skipping comment line\n");
+      #endif
+      continue;
+    }
+ 
+    /* First find parameter string and make a copy: */
+    /* start at beginning: */
+    param_begin = buf;
+    /* skip leading whitespace */
+    while (isspace(*param_begin))
+    {
+      ++param_begin;
+    }
+    /* now go from here to end of string, stopping at either */
+    /* whitespace or '=':   */
+    param_end = param_begin;
+    while (!isspace(*param_end)
+         && ('=' != (*param_end)))
+    {
+      ++param_end;
+    }
+
+    /* copy chars from start of non-whitespace up to '=': */
+    parameter = strndup(param_begin, (param_end - param_begin));
+
+    /* Now get value string: */
+    /* set value to first '=' in line: */
+    value = strchr(buf, '=');
+
+    #ifdef TUXMATH_DEBUG
+    if (!value || (value == buf))
+    {
+      fprintf(stderr, "Error while reading prefs - line with no '='!\n");
+      continue;
+    }
+    #endif
+
+    /* move past '=' sign: */
+    ++value;
+
+    /* skip leading whitespace */
+    while (isspace(*value))
+    { 
+      ++value;
+    }
+
+    value_end = value;
+
+    /* remove trailing whitespace or newline */
+    while (!isspace(*value_end)
+         && (0x0a != (*value_end))
+         && (*value_end))
+    {
+      ++value_end;
+    }
+    /* terminate string here: */
+    *value_end = 0;
+
+    #ifdef TUXMATH_DEBUG
+    printf("parameter = '%s'\t, length = %d\n", parameter, strlen(parameter));
+    printf("value = '%s'\t, length = %d\t, atoi() = %d\n", value, strlen(value), atoi(value));
+
+    #endif
+
+    /* Now ready to handle each name/value pair! */
+
+    /* Set general game_options struct (see tuxmath.h): */ 
+    /* FIXME should have error checking to make sure game_options->* */
+    /* settings are sane values (MC_Set*() functions already do this). */
+    if(0 == strcasecmp(parameter, "per_user_config")
+       && file_type == GLOBAL_CONFIG_FILE) 
+    /* Only let administrator change this setting */
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->per_user_config = v;
+    }
+
+    if(0 == strcasecmp(parameter, "use_sound"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->use_sound = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "fullscreen"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->fullscreen = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "use_bkgd"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->use_bkgd = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "demo_mode"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->demo_mode = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "oper_override"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->oper_override = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "use_keypad"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->use_keypad = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "speed"))
+    {
+      game_options->speed = atof(value);
+    }
+
+    else if(0 == strcasecmp(parameter, "allow_speedup"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->allow_speedup = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "speedup_factor"))
+    {
+      game_options->speedup_factor = atof(value);
+    }
+
+    else if(0 == strcasecmp(parameter, "max_speed"))
+    {
+      game_options->max_speed = atof(value);
+    }
+
+    else if(0 == strcasecmp(parameter, "slow_after_wrong"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        game_options->slow_after_wrong = v;
+    }
+
+    else if(0 == strcasecmp(parameter, "starting_comets"))
+    {
+      game_options->starting_comets = atoi(value);
+    }
+
+    else if(0 == strcasecmp(parameter, "extra_comets_per_wave"))
+    {
+      game_options->extra_comets_per_wave = atoi(value);
+    }
+
+    else if(0 == strcasecmp(parameter, "max_comets"))
+    {
+      game_options->max_comets = atoi(value);
+    }
+
+
+    /* Begin setting of math question options (see mathcards.h):   */ 
+
+    /* General math options */
+
+    else if(0 == strcasecmp(parameter, "allow_negatives"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetAllowNegatives(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "max_answer"))
+    {
+      MC_SetMaxAnswer(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_questions"))
+    {
+      MC_SetMaxQuestions(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "play_through_list"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetPlayThroughList(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "repeat_wrongs"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1) 
+        MC_SetRepeatWrongs(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "copies_repeated_wrongs"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetCopiesRepeatedWrongs(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "format_answer_last"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetFormatAnswerLast(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "format_answer_first"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetFormatAnswerFirst(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "format_answer_middle"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetFormatAnswerMiddle(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "format_answer_last"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetFormatAnswerLast(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "question_copies"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetQuestionCopies(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "randomize"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetRandomize(v);
+    }
+
+
+    /* Set the allowed math operations: */
+
+
+    else if(0 == strcasecmp(parameter, "addition_allowed"))
+    {
+      MC_SetAddAllowed(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "subtraction_allowed"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetSubAllowed(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "multiplication_allowed"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetMultAllowed(v);
+    }
+
+    else if(0 == strcasecmp(parameter, "division_allowed"))
+    {
+      int v = str_to_bool(value);
+      if (v != -1)
+        MC_SetDivAllowed(v);
+    }
+
+
+    /* Set min and max for addition: */
+
+
+    else if(0 == strcasecmp(parameter, "min_augend"))
+    {
+      MC_SetAddMinAugend(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_augend"))
+    {
+      MC_SetAddMaxAugend(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "min_addend"))
+    {
+      MC_SetAddMinAddend(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_addend"))
+    {
+      MC_SetAddMaxAddend(atoi(value));
+    }
+
+
+    /* Set min and max for subtraction: */
+
+
+    else if(0 == strcasecmp(parameter, "min_minuend"))
+    {
+      MC_SetSubMinMinuend(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_minuend"))
+    {
+      MC_SetSubMaxMinuend(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "min_subtrahend"))
+    {
+      MC_SetSubMinSubtrahend(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_subtrahend"))
+    {
+      MC_SetSubMaxSubtrahend(atoi(value));
+    }
+
+
+    /* Set min and max for multiplication: */
+
+
+    else if(0 == strcasecmp(parameter, "min_multiplier"))
+    {
+      MC_SetMultMinMultiplier(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_multiplier"))
+    {
+      MC_SetMultMaxMultiplier(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "min_multiplicand"))
+    {
+      MC_SetMultMinMultiplicand(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_multiplicand"))
+    {
+      MC_SetMultMaxMultiplicand(atoi(value));
+    }
+
+
+    /* Set min and max for division: */
+
+
+    else if(0 == strcasecmp(parameter, "min_divisor"))
+    {
+      MC_SetDivMinDivisor(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "min_quotient"))
+    {
+      MC_SetDivMinQuotient(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_divisor"))
+    {
+      MC_SetDivMaxDivisor(atoi(value));
+    }
+
+    else if(0 == strcasecmp(parameter, "max_quotient"))
+    {
+      MC_SetDivMaxQuotient(atoi(value));
+    }
+
+
+
+    else
+    {   
+      #ifdef TUXMATH_DEBUG
+      printf("parameter not recognized: %s\n", parameter);
+      #endif    
+
+/* All leftover options will be stored into the prefs
+		 setting hash (generic options -- should have had this
+		 idea much sooner... */
+// 	      int skip = 0;
+// 	      if (1)
+// 	      {line
+// 		  if(arg_comp (line, "itdb_", NULL) == 0)
+// 		  {   /* set incorrectly in 0.90 -- delete */
+// 		      skip = 1;
+// 		  }
+// 	      }
+// 	      if (!skip)
+// 		  prefs_set_string_value (line, arg);
+    }
+    free(parameter);
+  }
+  #ifdef TUXMATH_DEBUG
+  printf("\nAfter file read in:\n");
+  print_game_options(stdout, 0);
+  MC_PrintMathOptions(stdout, 0);
+  printf("Leaving read_config_file()\n");
+  #endif
+  return 1;
+}
+
+
+
+
+/* this function writes the settings for all game options to a */
+/* human-readable file.                                        */
+/* TODO write help on each setting into file.                  */
+
+int write_config_file(FILE *fp)
+{
+  #ifdef TUXMATH_DEBUG
+  printf("\nEntering write_config_file()\n");
+  #endif
+
+  /* get out if file pointer null */
+  if(!fp)
+  {
+    fprintf (stderr, "write_config_file() - file pointer invalid/n");
+
+    #ifdef TUXMATH_DEBUG
+    printf("Leaving write_config_file()\n");
+    #endif
+
+    return 0;
+  }
+
+  fprintf(fp, 
+          "############################################################\n"
+          "#                                                          #\n"
+          "#                 Tuxmath Config File                      #\n"
+          "#                                                          #\n"
+          "############################################################\n"
+          "\n"
+  );
+
+  /* print general game options (passing '1' as second arg causes */
+  /* "help" info for each option to be written to file as comments) */
+  print_game_options(fp, 1);
+  /* print options pertaining to math questions from MathCards: */
+  MC_PrintMathOptions(fp, 1);
+
+  #ifdef TUXMATH_DEBUG
+  printf("Leaving write_config_file()\n");
+  #endif
+
+  return 1;
+}
+
+
+
+/* Allows use of "true", "YES", T, etc. in text file for boolean values. */
+/* Return value of -1 means value string is not recognized.              */
+static int str_to_bool(char* val)
+{
+  char* ptr;
+
+  /* Check for recognized boolean strings: */
+  if ((0 == strcasecmp(val, "true"))
+    ||(0 == strcasecmp(val, "t"))
+    ||(0 == strcasecmp(val, "yes"))
+    ||(0 == strcasecmp(val, "y")))
+  {
+    return 1;
+  }
+
+  if ((0 == strcasecmp(val, "false"))
+    ||(0 == strcasecmp(val, "f"))
+    ||(0 == strcasecmp(val, "no"))
+    ||(0 == strcasecmp(val, "n")))
+  {
+    return 0;
+  }  
+
+  /* Return -1 if any chars are non-digits: */
+  ptr = val;
+  while (*ptr)
+  {
+    if (!isdigit(*ptr))
+      return -1;
+    ptr++;
+  }
+
+  /* If we get to here, val should be an integer. */
+  if (atoi(val))
+    return 1;
+  else
+    return 0;
+}

Added: tuxmath/trunk/src/config.h
===================================================================
--- tuxmath/trunk/src/config.h	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/config.h	2006-08-11 16:27:38 UTC (rev 20)
@@ -0,0 +1,26 @@
+/*
+C Interface: config.h
+
+Description: 
+
+
+Author: David Bruce <dbruce at tampabay.rr.com>, (C) 2006
+
+Copyright: See COPYING file that comes with this distribution (briefly, GNU GPL)
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#define PATH_MAX 4096
+
+enum {
+  USER_CONFIG_FILE,
+  GLOBAL_CONFIG_FILE
+};
+
+/* FIXME these should return errors if they fail. */
+int read_config_file(FILE* fp, int file_type);
+int write_config_file(FILE* fp);
+
+#endif

Modified: tuxmath/trunk/src/game.c
===================================================================
--- tuxmath/trunk/src/game.c	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/game.c	2006-08-11 16:27:38 UTC (rev 20)
@@ -83,6 +83,8 @@
 static int wave;
 static int score;
 static int pre_wave_score;
+static int prev_wave_comets;
+static int slowdown;
 static int num_attackers;
 static float speed;
 static int demo_countdown;
@@ -118,7 +120,7 @@
 static int check_exit_conditions(void);
 
 static void draw_numbers(char* str, int x, int y);
-static void draw_led_nums(void);
+static void draw_led_console(void);
 static void draw_question_counter(void);
 static void draw_console_image(int i);
 static void draw_line(int x1, int y1, int x2, int y2, int r, int g, int b);
@@ -413,7 +415,9 @@
   
   wave = 1;
   num_attackers = 2;
+  prev_wave_comets = game_options->starting_comets;
   speed = game_options->speed;
+  slowdown = 0;
   score = 0;
   demo_countdown = 1000;
   level_start_wait = LEVEL_START_WAIT_START;
@@ -738,8 +742,10 @@
       if (comets[i].y >= (screen->h - images[IMG_CITY_BLUE]->h) &&
 	  comets[i].expl < COMET_EXPL_END)
       {
+        /* Tell MathCards about it: */
+        MC_AnsweredIncorrectly(&(comets[i].flashcard));
+
         /* Disable shields or destroy city: */
-        MC_AnsweredIncorrectly(&(comets[i].flashcard));
         if (cities[comets[i].city].shields)
 	{
 	  cities[comets[i].city].shields = 0;
@@ -751,6 +757,14 @@
           playsound(SND_EXPLOSION);
         }
 
+        /* If slow_after_wrong selected, set flag to go back to starting speed and */
+        /* number of attacking comets: */
+        if (game_options->slow_after_wrong)
+        {
+          speed = game_options->speed;
+          slowdown = 1;
+        }
+
         tux_anim = IMG_TUX_FIST1;
         tux_anim_frame = ANIM_FRAME_START;
 
@@ -866,7 +880,7 @@
 
   /* If we are playing through a defined list of questions */
   /* without "recycling", display number of remaining questions: */
-  if (!MC_RecycleCorrects())
+  if (MC_PlayThroughList())
   {
     draw_question_counter();
   }
@@ -996,7 +1010,7 @@
   {
     /* pick image to draw: */
     int keypad_image;
-    if (MC_AllowNegAnswer())
+    if (MC_AllowNegatives())
     {
       /* draw regular keypad */
       keypad_image = IMG_KEYPAD;
@@ -1015,11 +1029,8 @@
     SDL_BlitSurface(images[keypad_image], NULL, screen, &dest);
   }
 
-  /* Draw console & tux: */
-  /* LED code moved into separate function by DSB */
-  /* this also draws the console                   */
-  draw_led_nums();
-
+  /* Draw console, LED numbers, & tux: */
+  draw_led_console();
   draw_console_image(tux_img);
  
   /* Swap buffers: */
@@ -1190,25 +1201,33 @@
 
   pre_wave_score = score;
 
-
   /* Set number of attackers for this wave: */
-  /* num_attackers = 2 * wave; */ 
 
-  /* increase number of comets and speed if desired */
-  if (game_options->allow_speedup)
+  /* On first wave or if slowdown flagged due to wrong answer: */
+  if (wave == 1 || slowdown)
   {
-    num_attackers = wave * game_options->extra_comets;
-    speed = speed * game_options->speedup_factor;
-
-    if (speed > game_options->max_speed)
-    {
-      speed = game_options->max_speed;
-    } 
+    prev_wave_comets = game_options->starting_comets;
+    speed = game_options->speed;
+    slowdown = 0;
   }
-  else
+  else /* Otherwise increase comets and speed if selected, not to */
+       /* exceed maximum:                                         */
   {
-    num_attackers = 2;
+    if (game_options->allow_speedup)
+    {
+      prev_wave_comets += game_options->extra_comets_per_wave;
+      if (prev_wave_comets > game_options->max_comets)
+      {
+        prev_wave_comets = game_options->max_comets;
+      } 
+      speed = speed * game_options->speedup_factor;
+      if (speed > game_options->max_speed)
+      {
+        speed = game_options->max_speed;
+      } 
+    }
   }
+  num_attackers = prev_wave_comets;
 }
 
 
@@ -1697,38 +1716,23 @@
   draw_numbers(str, nums_x, 0);
 }
 
-/* FIXME consider always using new image for simplicity */
 /* FIXME very confusing having this function draw console */
-/* Draw LED digits at the top of the screen: */
-/* Modified by DSB to display minus sign */
-void draw_led_nums(void)
+void draw_led_console(void)
 {
   int i;
   SDL_Rect src, dest;
   int y;
 
-  /* draw either just above console or at top of screen: */
-  if (MC_RecycleCorrects())
-  {
-    /* draw traditional console: */
-    draw_console_image(IMG_CONSOLE);
-    /* LEDs go at top in traditional TuxMath: */
-    y = 4;
+  /* draw new console image with "monitor" for LED numbers: */
+  draw_console_image(IMG_CONSOLE_LED);
+  /* set y to draw LED numbers into Tux's "monitor": */
+  y = (screen->h
+     - images[IMG_CONSOLE_LED]->h
+     + 4);  /* "monitor" has 4 pixel margin */       
 
-  }
-  else
-  {
-    /* draw new console image with "monitor" for LED numbers: */
-    draw_console_image(IMG_CONSOLE_LED);
-    /* draw LED numbers into "monitor": */
-    y = (screen->h
-       - images[IMG_CONSOLE_LED]->h
-       + 4);  /* "monitor" has 4 pixel margin */       
-  }
-
   /* begin drawing so as to center display depending on whether minus */
   /* sign needed (4 digit slots) or not (3 digit slots) DSB */
-  if (MC_AllowNegAnswer())
+  if (MC_AllowNegatives())
     dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 4) / 2);
   else
     dest.x = ((screen->w - ((images[IMG_LEDNUMS]->w) / 10) * 3) / 2);
@@ -1738,7 +1742,7 @@
   { 
     if (-1 == i)
     {
-      if (MC_AllowNegAnswer())
+      if (MC_AllowNegatives())
       {
         if (neg_answer_picked)
           src.x =  (images[IMG_LED_NEG_SIGN]->w) / 2;
@@ -1796,7 +1800,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 (MC_AllowNegAnswer())
+  if (MC_AllowNegatives())
   {
     if (!images[IMG_KEYPAD])
       return;
@@ -1979,14 +1983,14 @@
   }
   /* support for negative answer input DSB */
   else if ((key == SDLK_MINUS || key == SDLK_KP_MINUS)
-        && MC_AllowNegAnswer())  /* do nothing unless neg answers allowed */
+        && MC_AllowNegatives())  /* do nothing unless neg answers allowed */
   {
     /* allow player to make answer negative: */
     neg_answer_picked = 1;
     tux_pressing = 1;
   }
   else if ((key == SDLK_PLUS || key == SDLK_KP_PLUS)
-         && MC_AllowNegAnswer())  /* do nothing unless neg answers allowed */
+         && MC_AllowNegatives())  /* do nothing unless neg answers allowed */
   {
     /* allow player to make answer positive: */
     neg_answer_picked = 0;
@@ -2023,7 +2027,7 @@
 void reset_comets(void)
 {
   int i =0;
-  for (i = 0; i < MAX_COMETS; i++)
+  for (i = 0; i < game_options->max_comets; i++)
   {
     comets[i].alive = 0;
     comets[i].expl = 0;

Modified: tuxmath/trunk/src/mathcards.c
===================================================================
--- tuxmath/trunk/src/mathcards.c	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/mathcards.c	2006-08-11 16:27:38 UTC (rev 20)
@@ -36,12 +36,10 @@
 /* to give file scope rather than extern scope.          */
 
 static MC_MathQuestion* generate_list(void);
+static void clear_negatives(void);
 static int validate_question(int n1, int n2, int n3);
 static MC_MathQuestion* create_node(int n1, int n2, int op, int ans, int f);
 static MC_MathQuestion* create_node_from_card(MC_FlashCard* card);
-static MC_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);
@@ -56,13 +54,17 @@
 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);
+/* these functions are dead code unless compiling with debug turned on: */
+#ifdef MC_DEBUG 
 static void print_node(MC_MathQuestion* ptr);
 static void print_card(MC_FlashCard card);
 static void print_counters(void);
+static MC_MathQuestion* create_node_copy(MC_MathQuestion* other);
+static int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy);
+static MC_FlashCard*    create_card_from_node(MC_MathQuestion* node);
+static void print_list(MC_MathQuestion* list);
+#endif
 
-/* 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  */
@@ -83,7 +85,7 @@
 
     #ifdef MC_DEBUG
     printf("\nAlready initialized");  
-    print_math_options(); 
+    MC_PrintMathOptions(stdout, 0); 
     printf("\nLeaving MC_Initialize()\n");
     #endif
 
@@ -104,12 +106,12 @@
   } 
 
   /* set general math options */
-  math_opts->allow_neg_answer = DEFAULT_ALLOW_NEG_ANSWER;
+  math_opts->play_through_list = DEFAULT_PLAY_THROUGH_LIST;
+  math_opts->repeat_wrongs = DEFAULT_REPEAT_WRONGS;
+  math_opts->copies_repeated_wrongs = DEFAULT_COPIES_REPEATED_WRONGS;
+  math_opts->allow_negatives = DEFAULT_ALLOW_NEGATIVES;
   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;
@@ -141,48 +143,15 @@
   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->allow_negatives)
   {
-    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;
+    clear_negatives();
+  }
 
-    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(); 
+  MC_PrintMathOptions(stdout, 0); 
   printf("\nLeaving MC_Initialize()\n");
   #endif 
 
@@ -190,6 +159,7 @@
 }
 
 
+
 /*  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.       */
@@ -419,7 +389,7 @@
   answered_correctly++;
   questions_pending--;
 
-  if (math_opts->recycle_corrects)
+  if (!math_opts->play_through_list)
   /* reinsert question into question list at random location */
   {
     #ifdef MC_DEBUG
@@ -503,16 +473,16 @@
   }
 
   /* if desired, put question back in list so student sees it again */
-  if (math_opts->recycle_wrongs)
+  if (math_opts->repeat_wrongs)
   {
     int i;
 
     #ifdef MC_DEBUG
-    printf("\nAdding %d copies to question_list:", math_opts->copies_recycled_wrongs);
+    printf("\nAdding %d copies to question_list:", math_opts->copies_repeated_wrongs);
     #endif
  
     /* can put in more than one copy (to drive the point home!) */
-    for (i = 0; i < math_opts->copies_recycled_wrongs; i++)
+    for (i = 0; i < math_opts->copies_repeated_wrongs; i++)
     {  
       ptr1 = create_node_from_card(fc);
       ptr2 = pick_random(quest_list_length, question_list);
@@ -521,15 +491,15 @@
     }
     /* 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);
+    unanswered += (math_opts->copies_repeated_wrongs - 1);
   }
   else
   {
     #ifdef MC_DEBUG
-    printf("\nnot recycling wrong answers\n");
+    printf("\nnot repeating wrong answers\n");
     #endif
 
-    /* not recycling questions so list gets shorter:      */
+    /* not repeating questions so list gets shorter:      */
     unanswered--;
   }
 
@@ -548,7 +518,7 @@
 int MC_MissionAccomplished(void)
 {
   if (starting_length
-    && math_opts->recycle_wrongs
+    && math_opts->repeat_wrongs
     && !unanswered)
   {
     return 1;
@@ -607,44 +577,63 @@
 }
 
 
-void MC_SetAllowNegAnswer(int opt)
+void MC_SetMaxQuestions(int max)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_SetAllowNegAnswer(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_SetMaxQuestions(): math_opts not valid!\n");
     return;
   }
-  math_opts->allow_neg_answer = int_to_bool(opt);
+  if (max < 0)
+  {
+    fprintf(stderr, "\nMC_SetMaxQuestions(): max_questions cannot be negative!\n");
+    return;
+  }
+  math_opts->max_questions = max;
 }
 
+void MC_SetAllowNegatives(int opt)
+{
+  if (!math_opts)
+  {
+    fprintf(stderr, "\nMC_SetAllowNegatives(): math_opts not valid!\n");
+    return;
+  }
+  math_opts->allow_negatives = int_to_bool(opt);
+  if (!opt)
+  {
+    clear_negatives();
+  }
+}
 
-void MC_SetRecycleCorrects(int opt)
+
+void MC_SetPlayThroughList(int opt)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_SetRecycleCorrects(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_SetPlayThroughList(): math_opts not valid!\n");
     return;
   }
-  math_opts->recycle_corrects = int_to_bool(opt);
+  math_opts->play_through_list = int_to_bool(opt);
 }
 
 
-void MC_SetRecycleWrongs(int opt)
+void MC_SetRepeatWrongs(int opt)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_SetRecycleWrongs(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_SetRepeatWrongs(): math_opts not valid!\n");
     return;
   }
-  math_opts->recycle_wrongs = int_to_bool(opt);
+  math_opts->repeat_wrongs = int_to_bool(opt);
 }
 
 
-void MC_SetCopiesRecycledWrongs(int copies)
+void MC_SetCopiesRepeatedWrongs(int copies)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_SetCopiesRecycledWrongs(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_SetCopiesRepeatedWrongs(): math_opts not valid!\n");
     return;
   }
   /* number of copies must be between 1 and 10: */
@@ -652,7 +641,7 @@
     copies = 1;
   if (copies > 10)
     copies = 10;
-  math_opts->copies_recycled_wrongs = copies;
+  math_opts->copies_repeated_wrongs = copies;
 }
 
 /*NOTE - list can contain more than one format */
@@ -1021,47 +1010,47 @@
   return math_opts->max_answer;
 }
 
-int MC_AllowNegAnswer(void)
+int MC_AllowNegatives(void)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_AllowNegAnswer(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_AllowNegatives(): math_opts not valid!\n");
     return MC_MATH_OPTS_INVALID;
   }
-  return math_opts->allow_neg_answer;
+  return math_opts->allow_negatives;
 }
 
 
-int MC_RecycleCorrects(void)
+int MC_PlayThroughList(void)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_RecycleCorrects(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_PlayThroughList(): math_opts not valid!\n");
     return MC_MATH_OPTS_INVALID;
   }
-  return math_opts->recycle_corrects;
+  return math_opts->play_through_list;
 }
 
 
-int MC_RecycleWrongs(void)
+int MC_RepeatWrongs(void)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_RecycleWrongs(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_RepeatWrongs(): math_opts not valid!\n");
     return MC_MATH_OPTS_INVALID;
   }
-  return math_opts->recycle_wrongs;
+  return math_opts->repeat_wrongs;
 }
 
 
-int MC_CopiesRecycledWrongs(void)
+int MC_CopiesRepeatedWrongs(void)
 {
   if (!math_opts)
   {
-    fprintf(stderr, "\nMC_CopiesRecycledWrongs(): math_opts not valid!\n");
+    fprintf(stderr, "\nMC_CopiesRepeatedWrongs(): math_opts not valid!\n");
     return MC_MATH_OPTS_INVALID;
   }
-  return math_opts->copies_recycled_wrongs;
+  return math_opts->copies_repeated_wrongs;
 }
 
 
@@ -1359,6 +1348,46 @@
 of this file) */
 
 
+/* Resets negative values to zero - used when allow_negatives deselected. */
+void clear_negatives(void)
+{
+  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;
+}
+
 /* 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 very redundant code - figure out way to iterate through different */
@@ -1374,7 +1403,7 @@
 
   #ifdef MC_DEBUG
   printf("\nEntering generate_list()");
-  print_math_options();
+  MC_PrintMathOptions(stdout, 0);
   #endif
  
   /* add nodes for each math operation allowed */
@@ -1656,7 +1685,7 @@
     return 0;
   }
   /* make sure none of values are negative if negatives not allowed: */
-  if (!math_opts->allow_neg_answer)
+  if (!math_opts->allow_negatives)
   {
     if (n1 < 0 || n2 < 0 || n3 < 0)
     {
@@ -1682,7 +1711,7 @@
 }
 
 
-
+#ifdef MC_DEBUG
 /* 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)
@@ -1700,9 +1729,9 @@
   ptr->previous = 0;
   return ptr;
 }
+#endif
 
 
-
 MC_MathQuestion* create_node_from_card(MC_FlashCard* flashcard)
 {
   MC_MathQuestion* ptr;
@@ -1719,8 +1748,7 @@
   return ptr;
 }
 
-
-
+#ifdef MC_DEBUG
 MC_FlashCard* create_card_from_node(MC_MathQuestion* node)
 {
   MC_FlashCard* fc;
@@ -1734,9 +1762,9 @@
   fc->format = node->card.format;
   return fc;
 }
+#endif
 
-
-
+#ifdef MC_DEBUG
 /* this one copies the contents, including pointers; both nodes must be allocated */
 int copy_node(MC_MathQuestion* original, MC_MathQuestion* copy)
 {
@@ -1755,9 +1783,9 @@
   copy->previous = original->previous;
   return 1;
 }
+#endif
 
 
-
 /* 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)
@@ -1850,60 +1878,173 @@
 
 
 
-
-/* prints struct to stdout for testing purposes */
-void print_math_options(void)
+/* FIXME generalize this so it can be used to write config file */
+/* prints struct to file */
+void MC_PrintMathOptions(FILE* fp, int verbose)
 {
   printf("\nprint_math_options():\n");
 
  /* bail out if no struct */
   if (!math_opts)
   {
-    printf("\nMath Options struct does not exist!\n");
+    fprintf(stderr, "\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("recycle_corrects:\t%d\n", math_opts->recycle_corrects);
-  printf("recycle_wrongs:\t%d\n", math_opts->recycle_wrongs);
-  printf("copies_recycled_wrongs:\t%d\n", math_opts->copies_recycled_wrongs);
-  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);
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "#                                                          #\n"
+                 "#                  General Math Options                    #\n"
+                 "#                                                          #\n"
+                 "# If 'play_through_list' selected, Tuxmath will ask each   #\n"
+                 "# question in the defined list. The game ends when no      #\n"
+                 "# questions remain.  Default is 1 (i.e. 'true' or 'yes').  #\n"
+                 "#                                                          #\n"
+                 "# 'question_copies' is the number of times each question   #\n"
+                 "# will be asked. It can be 1 to 10 - Default is 1.         #\n"
+                 "#                                                          #\n"
+                 "# 'repeat_wrongs' tells Tuxmath whether to reinsert        #\n"
+                 "# incorrectly answered questions into the list to be       #\n"
+                 "# asked again. Default is 1 (yes).                         #\n"
+                 "#                                                          #\n"
+                 "# 'copies_repeated_wrongs' gives the number of times an    #\n"
+                 "# incorrectly answered question will reappear. Default     #\n"
+                 "# is 1.                                                    #\n"
+                 "#                                                          #\n"
+                 "# The defaults for these values result in a 'mission'      #\n" 
+                 "# for Tux that is accomplished by answering all            #\n"
+                 "# questions correctly with at least one surviving city.    #\n"
+                 "############################################################\n\n");
+  }  
+  fprintf (fp, "play_through_list = %d\n", math_opts->play_through_list);
+  fprintf (fp, "question_copies = %d\n", math_opts->question_copies);
+  fprintf (fp, "repeat_wrongs = %d\n", math_opts->repeat_wrongs);
+  fprintf (fp, "copies_repeated_wrongs = %d\n", math_opts->copies_repeated_wrongs);
 
-  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);
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "# 'format_answer_last' (, _first, middle) control the      #\n"
+                 "# generation of questions with the answer in different     #\n"
+                 "# places in the equation.  i.e.:                           #\n"
+                 "#                                                          #\n"
+                 "#    format_answer_last:    2 + 2 = ?                      #\n"
+                 "#    format_answer_first:   ? + 2 = 4                      #\n"
+                 "#    format_answer_middle:  2 + ? = 4                      #\n"
+                 "#                                                          #\n"
+                 "# By default, 'format_answer_first' is enabled and the     #\n"
+                 "# other two formats are disabled.  Note that the options   #\n"
+                 "# are not mutually exclusive - the question list may       #\n"
+                 "# contain questions with different formats.                #\n"
+                 "############################################################\n\n");
+  }  
+  fprintf (fp, "format_answer_last = %d\n", math_opts->format_answer_last);
+  fprintf (fp, "format_answer_first = %d\n", math_opts->format_answer_first);
+  fprintf (fp, "format_answer_middle = %d\n", math_opts->format_answer_middle);
 
-  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);
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "# 'allow_negatives' allows or disallows use of negative    #\n"
+                 "# numbers as both operands and answers.  Default is 0      #\n"
+                 "# (no), which disallows questions like:                    #\n"
+                 "#          2 - 4 = ?                                       #\n"
+                 "# Note: this option must be enabled in order to set the    #\n"
+                 "# operand ranges to include negatives (see below). If it   #\n"
+                 "# is changed from 1 (yes) to 0 (no), any negative          #\n"
+                 "# operand limits will be reset to 0.                       #\n"
+                 "############################################################\n\n");
+  }  
+  fprintf (fp, "allow_negatives = %d\n", math_opts->allow_negatives);
 
-  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);
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "# 'max_answer' is the largest absolute value allowed in    #\n"
+                 "# any value in a question (not only the answer). Default   #\n"
+                 "# is 144. It can be set as high as 999.                    #\n"
+                 "############################################################\n\n");
+  }  
+  fprintf (fp, "max_answer = %d\n", math_opts->max_answer);
 
-  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);
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "# 'max_questions' is limit of the length of the question   #\n"
+                 "# list. Default is 5000 - only severe taskmasters will     #\n"
+                 "# need to raise it.                                        #\n"
+                 "############################################################\n\n");
+  }  
+  fprintf (fp, "max_questions = %d\n", math_opts->max_questions);  
+
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "# If 'randomize' selected, the list will be shuffled       #\n"
+                 "# at the start of the game.  Default is 1 (yes).           #\n"
+                 "############################################################\n\n");
+  }
+  fprintf (fp, "randomize = %d\n", math_opts->randomize);
+
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "#                                                          #\n"
+                 "#                 Math Operations Allowed                  #\n"
+                 "#                                                          #\n"
+                 "# These options enable questions for each of the four math #\n"
+                 "# operations.  All are 1 (yes) by default.                 #\n"
+                 "############################################################\n\n");
+  }
+  fprintf(fp, "addition_allowed = %d\n", math_opts->addition_allowed);
+  fprintf(fp, "subtraction_allowed = %d\n", math_opts->subtraction_allowed);
+  fprintf(fp, "multiplication_allowed = %d\n", math_opts->multiplication_allowed);
+  fprintf(fp, "division_allowed = %d\n", math_opts->division_allowed);
+
+
+  if (verbose)
+  {
+    fprintf (fp, "\n############################################################\n"
+                 "#                                                          #\n"
+                 "#      Minimum and Maximum Values for Operand Ranges       #\n"
+                 "#                                                          #\n"
+                 "# Operand limits can be set to any integer up to the       #\n"
+                 "# value of 'max_answer'.  If 'allow_negatives' is set to 1 #\n"
+                 "# (yes), either negative or positive values can be used.   #\n"
+                 "# Tuxmath will generate questions for every value in the   #\n"
+                 "# specified range. The maximum must be greater than or     #\n"
+                 "# equal to the corresponding minimum for any questions to  #\n"
+                 "# be generated for that operation.                         #\n"
+                 "############################################################\n\n");
+  }
+  fprintf(fp, "\n# Addition operands: augend + addend = sum\n");
+  fprintf(fp, "min_augend = %d\n", math_opts->min_augend);
+  fprintf(fp, "max_augend = %d\n", math_opts->max_augend);
+  fprintf(fp, "min_addend = %d\n", math_opts->min_addend);
+  fprintf(fp, "max_addend = %d\n", math_opts->max_addend);
+
+  fprintf(fp, "\n# Subtraction operands: minuend - subtrahend = difference\n");
+  fprintf(fp, "min_minuend = %d\n", math_opts->min_minuend);
+  fprintf(fp, "max_minuend = %d\n", math_opts->max_minuend);
+  fprintf(fp, "min_subtrahend = %d\n", math_opts->min_subtrahend);
+  fprintf(fp, "max_subtrahend = %d\n", math_opts->max_subtrahend);
+
+  fprintf(fp, "\n# Multiplication operands: multiplier * multiplicand = product\n");
+  fprintf(fp, "min_multiplier = %d\n", math_opts->min_multiplier);
+  fprintf(fp, "max_multiplier = %d\n", math_opts->max_multiplier);
+  fprintf(fp, "min_multiplicand = %d\n", math_opts->min_multiplicand);
+  fprintf(fp, "max_multiplicand = %d\n", math_opts->max_multiplicand);
+
+  fprintf(fp, "\n# Division operands: dividend/divisor = quotient\n");
+  fprintf(fp, "min_divisor = %d\n",math_opts->min_divisor);
+  fprintf(fp, "max_divisor = %d\n", math_opts->max_divisor);
+  fprintf(fp, "min_quotient = %d\n", math_opts->min_quotient);
+  fprintf(fp, "max_quotient = %d\n", math_opts->max_quotient);
 }
 
 
-
+#ifdef MC_DEBUG
 void print_list(MC_MathQuestion* list)
 {
   if (!list)
@@ -1920,9 +2061,9 @@
     ptr = ptr->next;
   }
 }
+#endif
 
 
-
 void print_node(MC_MathQuestion* ptr)
 {
   if (ptr)
@@ -1934,7 +2075,7 @@
 }
 
 
-
+#ifdef MC_DEBUG
 void print_card(MC_FlashCard card)
 {
   printf("\nprint_card():");
@@ -1945,10 +2086,10 @@
            card.num3,
            card.format);
 }
+#endif
 
 
-
-
+#ifdef MC_DEBUG
 /* This sends the values of all "global" counters and the */
 /* lengths of the question lists to stdout - for debugging */
 void print_counters(void)
@@ -1962,6 +2103,7 @@
   printf("\nlist_length(wrong_quests) = \t%d", list_length(wrong_quests));
   printf("\nquestions_pending = \t%d", questions_pending);
 }
+#endif
 
 int list_length(MC_MathQuestion* list)
 {
@@ -2114,7 +2256,7 @@
   
   if (i < 0 
    && math_opts
-   && !math_opts->allow_neg_answer)
+   && !math_opts->allow_negatives)
   {
     i = 0;
   }

Modified: tuxmath/trunk/src/mathcards.h
===================================================================
--- tuxmath/trunk/src/mathcards.h	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/mathcards.h	2006-08-11 16:27:38 UTC (rev 20)
@@ -15,7 +15,7 @@
 #ifndef MATHCARDS_H
 #define MATHCARDS_H
 
-#define MC_DEBUG
+//#define MC_DEBUG
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -41,12 +41,12 @@
 /* math questions are asked during a game */
 typedef struct MC_Options {
   /* general math options */
-  int allow_neg_answer;
+  int play_through_list;
+  int repeat_wrongs;
+  int copies_repeated_wrongs;
+  int allow_negatives;
   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                                               */
@@ -79,16 +79,17 @@
 } 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 MC_GLOBAL_MAX 999                 /* this is the largest absolute value that */
+                                          /* can be entered 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_PLAY_THROUGH_LIST 1       /* play until all questions in list answered */                                                 /* correctly */
+#define DEFAULT_REPEAT_WRONGS   1        /* reuse incorrectly answered questions or not */
+#define DEFAULT_COPIES_REPEATED_WRONGS 1  /* how many copies of an incorrectly answered */                                                /* question to re-insert*/
+#define DEFAULT_ALLOW_NEGATIVES 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 */
@@ -96,7 +97,7 @@
 #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_MIN_AUGEND 0              /* augend + addend = sum */
 #define DEFAULT_MAX_AUGEND 12
 #define DEFAULT_MIN_ADDEND 0
 #define DEFAULT_MAX_ADDEND 12
@@ -207,22 +208,27 @@
 /*  user interface program exits.                         */
 void MC_EndGame(void);
 
+/*  Prints contents of math_opts struct in human-readable   */
+/*  form to given file. "verbose" tells the function to     */
+/*  write a lot of descriptive "help"-type info for each    */
+/*  option (intended to make config files self-documenting).*/
+void MC_PrintMathOptions(FILE* fp, int verbose);
 
-
 /* Simple "Set/Get" type functions for option parameters: */
 
 /* Simple functions to set option parameters: */
 
 /* Set general math options:   */
+void MC_SetPlayThroughList(int opt);
+void MC_SetRepeatWrongs(int opt);
+void MC_SetQuestionCopies(int copies);         /* how many times each question is put in list */
+void MC_SetCopiesRepeatedWrongs(int copies);
 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_SetMaxQuestions(int max);
+void MC_SetAllowNegatives(int opt);
 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: */
@@ -267,11 +273,11 @@
 /* "Get" type functions to query option parameters: */
 
 /* Query general math options: */
+int MC_PlayThroughList(void);
+int MC_RepeatWrongs(void);
+int MC_CopiesRepeatedWrongs(void);
 int MC_MaxAnswer(void);
-int MC_AllowNegAnswer(void);
-int MC_RecycleCorrects(void);
-int MC_RecycleWrongs(void);
-int MC_CopiesRecycledWrongs(void);
+int MC_AllowNegatives(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                                               */

Modified: tuxmath/trunk/src/setup.c
===================================================================
--- tuxmath/trunk/src/setup.c	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/setup.c	2006-08-11 16:27:38 UTC (rev 20)
@@ -10,11 +10,16 @@
   bill at newbreedsoftware.com
   http://www.newbreedsoftware.com/
 
+  Modified by David Bruce
+  dbruce at tampabay.rr.com
 
   Part of "Tux4Kids" Project
-  http://www.tux4kids.org/
-   
-  August 26, 2001 - January 3, 2005
+  http://www.tux4kids.net/
+  Subversion repository:
+  https://svn.tux4kids.net/tuxmath
+
+ 
+  August 26, 2001 - August 8, 2006.
 */
 
 
@@ -23,18 +28,23 @@
 #include <string.h>
 #include <stdlib.h>
 
+/* FIXME maybe unistd.h not needed, even less sure about portability */
+//#include <unistd.h>
+
 #include <SDL.h>
+
 #ifndef NOSOUND
 #include <SDL_mixer.h>
 #endif
+
 #include <SDL_image.h>
 
 #include "mathcards.h"
 
-#include "tuxmath.h"
 #include "setup.h"
 #include "images.h"
 #include "sounds.h"
+#include "config.h"
 #include "game.h"
 
 
@@ -173,6 +183,8 @@
 Mix_Music * musics[NUM_MUSICS];
 #endif
 
+FILE* config_file;
+
 int opers[NUM_OPERS], range_enabled[NUM_Q_RANGES];
 
 game_option_type* game_options;
@@ -181,9 +193,7 @@
 
 void seticon(void);
 void usage(int err, char * cmd);
-
 int initialize_game_options(game_option_type* opts);
-void print_game_options(game_option_type* opts);
 
 /* --- Set-up function! --- */
 
@@ -194,6 +204,7 @@
 
   screen = NULL;
 
+  /* Initialize MathCards backend for math questions: */
   if (!MC_Initialize())
   {
     printf("\nUnable to initialize MathCards\n");
@@ -201,7 +212,6 @@
     exit(1);
   }
 
-
   /* initialize game_options struct with defaults DSB */
   game_options = malloc(sizeof(game_option_type));
   if (!initialize_game_options(game_options))
@@ -211,7 +221,48 @@
     exit(1);
   }
 
+  /* Now that MathCards and game_options initialized using  */
+  /* hard-coded defaults, read options from disk and mofify */
+  /* as needed. First read in installation-wide settings:   */
+  config_file = fopen(DATA_PREFIX ".tuxmath", "r");
+  if (config_file)
+  {
+    read_config_file(config_file, GLOBAL_CONFIG_FILE);
+    fclose(config_file);
+    config_file = NULL;
+  }
 
+  /* Now read in user-specific settings, if desired.  By    */
+  /* default, this restores settings from the player's last */
+  /* game:                                                  */
+  if (game_options->per_user_config)
+  {
+    /* find $HOME and tack on file name: */
+    char* home_dir;
+    home_dir = malloc(sizeof(char)*PATH_MAX);
+
+    strcpy(home_dir, getenv("HOME"));
+
+    #ifdef TUXMATH_DEBUG
+    printf("\nIn setup() home directory is: = %s\n", home_dir);
+    #endif
+
+    strcat(home_dir, "/.tuxmath");
+
+    #ifdef TUXMATH_DEBUG
+    printf("\nIn setup() config file: = %s\n", home_dir);
+    #endif
+
+    config_file = fopen(home_dir, "r");
+    if (config_file)
+    {
+      read_config_file(config_file, USER_CONFIG_FILE);
+      fclose(config_file);
+      config_file = NULL;
+    }
+    free(home_dir);
+  }
+
   for (i = 0; i < NUM_Q_RANGES; i++)
   { 
     range_enabled[i] = 1;
@@ -256,9 +307,17 @@
         "If that city is hit by another comet, it is destroyed completely.\n"
 	"When you lose all of your cities, the game ends.\n\n");
 
+      printf("Note: all settings are now stored in a config file named '.tuxmath' in\n"
+             "the user's home directory as simple name/value pairs. It is much easier\n"
+             "to edit this file to set game parameters than to use the command-line\n"
+             "arguments listed below. Also, many options are not selectable from the\n"
+             "command line. The config file contains extensive comments detailing how\n"
+             "to configure the behavior of Tuxmath.\n\n");
+
       printf("Run the game with:\n"
-        "--norepeats      - to ask each question only once, allowing player to\n"
-        "                   win game if all questions successfully answered\n"
+        "--playthroughlist      - to ask each question only once, allowing player to\n"
+        "                         win game if all questions successfully answered\n"
+
         "--answersfirst   - to ask questions in format: ? + num2 = num3\n"
         "                   instead of default format: num1 + num2 = ?\n"
         "--answersmiddle  - to ask questions in format: num1 + ? = num3\n"
@@ -347,12 +406,12 @@
     else if (strcmp(argv[i], "--allownegatives") == 0 ||
              strcmp(argv[i], "-n") == 0)
     {
-      MC_SetAllowNegAnswer(1);
+      MC_SetAllowNegatives(1);
     }
-    else if (strcmp(argv[i], "--norepeats") == 0 ||
-             strcmp(argv[i], "-r") == 0)
+    else if (strcmp(argv[i], "--playthroughlist") == 0 ||
+             strcmp(argv[i], "-l") == 0)
     {
-      MC_SetRecycleCorrects(0);
+      MC_SetPlayThroughList(1);
     }
     else if (strcmp(argv[i], "--answersfirst") == 0)
     {
@@ -430,8 +489,6 @@
     game_options->use_keypad = 0;
   }
 
-
-  
   /* Init SDL Video: */
 
   if (SDL_Init(SDL_INIT_VIDEO) < 0)
@@ -443,9 +500,6 @@
       exit(1);
     }
 
-  printf("before SDL Audio\n");
-
-
   /* Init SDL Audio: */
 
 #ifndef NOSOUND
@@ -462,11 +516,9 @@
         }
     }
 
-   printf("middle of Audio\n");
  
   if (game_options->use_sound)
   {
-    printf("using sound \n"); 
        if (Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 2048) < 0)
         {
           printf( "\nWarning: I could not set up audio for 44100 Hz "
@@ -485,7 +537,6 @@
 
 #endif
 
-  printf("after SDL Audio\n");
  
 
   if (game_options->fullscreen)
@@ -583,7 +634,6 @@
     SDL_Flip(screen);
   }
 
-  printf("all images loaded\n");
 
 #ifndef NOSOUND
   if (game_options->use_sound)
@@ -655,7 +705,35 @@
 
 /* free any heap memory used during game DSB */
 void cleanup()
+
 {
+  /* find $HOME and tack on file name: */
+  char* home_dir;
+  home_dir = malloc(sizeof(char)*PATH_MAX);
+
+  strcpy(home_dir, getenv("HOME"));
+
+  #ifdef TUXMATH_DEBUG
+  printf("\nIn cleanup() home directory is: = %s\n", home_dir);
+  #endif
+
+  strcat(home_dir, "/.tuxmath");
+
+  #ifdef TUXMATH_DEBUG
+  printf("\nIn cleanup() config file: = %s\n", home_dir);
+  #endif
+
+  /* save settings: */
+  config_file = fopen(home_dir, "w");
+  if (config_file)
+  {
+    write_config_file(config_file);
+    fclose(config_file);
+    config_file = NULL;
+  }
+  free(home_dir);
+
+
   if (game_options)
     free(game_options);
   /* frees any heap used by MathCards: */
@@ -671,6 +749,7 @@
     return 0;
 
   /* set general game options */
+  opts->per_user_config = DEFAULT_PER_USER_CONFIG;
   opts->use_sound = DEFAULT_USE_SOUND;
   opts->fullscreen = DEFAULT_FULLSCREEN;
   opts->use_bkgd = DEFAULT_USE_BKGD;
@@ -682,46 +761,164 @@
   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->starting_comets = DEFAULT_STARTING_COMETS;
+  opts->extra_comets_per_wave = DEFAULT_EXTRA_COMETS_PER_WAVE;
   opts->max_comets = DEFAULT_MAX_COMETS;
   opts->num_cities = DEFAULT_NUM_CITIES;   /* MUST BE AN EVEN NUMBER! */
   opts->num_bkgds = DEFAULT_NUM_BKGDS;
   opts->max_city_colors = DEFAULT_MAX_CITY_COLORS;
 
-  /* for testing purposes */
-  /* print_game_options(opts); */
+  #ifdef TUXMATH_DEBUG
+  print_game_options(stdout, 0);
+  #endif
+
   return 1;
 }
 
 
 
-/* prints struct to stdout for testing purposes */
-void print_game_options(game_option_type* opts)
+/* prints struct to stream for testing purposes */
+/* TODO include more info/help about these options in output */
+void print_game_options(FILE* fp, int verbose)
 {
  /* bail out if no struct */
-  if (!opts)
+  if (!game_options)
+  {
+    fprintf(stderr, "print_game_options(): invalid game_option_type struct");
     return;
+  }
 
-  printf("\nPrinting members of game_options struct:\n");
-  printf("\nGeneral game options:\n");
-  printf("use_sound:\t%d\n", opts->use_sound);
-  printf("fullscreen:\t%d\n", opts->fullscreen);
-  printf("use_bkgd:\t%d\n", opts->use_bkgd);
-  printf("demo_mode:\t%d\n", opts->demo_mode);
-  printf("oper_override:\t%d\n", opts->oper_override);
-  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);
+  if(verbose)
+  {
+    fprintf (fp, "\n############################################################\n" 
+                 "#                                                          #\n"
+                 "#                 General Game Options                     #\n"
+                 "#                                                          #\n"
+                 "# The following options are boolean (true/false) variables #\n"
+                 "# that control various aspects of Tuxmath's behavior.      #\n"
+                 "# The program writes the values to the file as either '0'  #\n"
+                 "# or '1'. However, the program accepts 'n', 'no', 'f', and #\n"
+                 "# 'false' as synonyms for '0', and similarly accepts 'y',  #\n"
+                 "# 'yes', 't', and 'true' as synonyms for '1' (all case-    #\n"
+                 "# insensitive).                                            #\n"
+                 "############################################################\n\n");
+  }
+
+  if(verbose)
+  {
+    fprintf (fp, "############################################################\n" 
+                 "# 'per_user_config' determines whether Tuxmath will look   #\n"
+                 "# in the user's home directory for settings. Default is 1  #\n"
+                 "# (yes). If deselected, the program will ignore the user's #\n"
+                 "# .tuxmath file and use the the global settings in the     #\n"
+                 "# installation-wide config file.                           #\n"
+                 "# This setting cannot be changed by an ordinary user.      #\n"
+                 "############################################################\n");
+  }
+  fprintf(fp, "per_user_config = %d\n", game_options->per_user_config);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Self-explanatory, default is 1:\n");
+  }
+  fprintf(fp, "use_sound = %d\n", game_options->use_sound);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Use either fullscreen at 640x480 resolution or window of that size\n"
+                 "# Default is 1.  Change to 0 if SDL has trouble with fullscreen.\n");
+  } 
+  fprintf(fp, "fullscreen = %d\n", game_options->fullscreen);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Use 640x480 jpg image for background; default is 1.\n");
+  }
+  fprintf(fp, "use_bkgd = %d\n", game_options->use_bkgd);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Program runs as demo; default is 0.\n");
+  }
+  fprintf(fp, "demo_mode = %d\n", game_options->demo_mode);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Use operator selection from command line; default is 0.\n");
+  }
+  fprintf(fp, "oper_override = %d\n", game_options->oper_override);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Display onscreen numeric keypad; default is 0.\n");
+  }
+  fprintf(fp, "use_keypad = %d\n", game_options->use_keypad);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n############################################################\n" 
+                 "# The remaining settings determine the speed and number    #\n"
+                 "# of comets.  The speed settings are float numbers (mean-  #\n"
+                 "# ing decimals allowed). The comet settings are integers.  #\n"
+                 "############################################################\n");
+  }
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Starting comet speed. Default is 1.\n");
+  }
+  fprintf(fp, "speed = %f\n", game_options->speed);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Speed is multiplied by this factor with each new wave.\n"
+                 "# Default is 1.2\n");
+  }
+  fprintf(fp, "speedup_factor = %f\n", game_options->speedup_factor);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Maximum speed. Default is 10.\n");
+  }
+  fprintf(fp, "max_speed = %f\n", game_options->max_speed);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Number of comets for first wave. Default is 2.\n");
+  }
+  fprintf(fp, "starting_comets = %d\n", game_options->starting_comets);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Comets to add for each successive wave. Default is 2.\n");
+  }
+  fprintf(fp, "extra_comets_per_wave = %d\n", game_options->extra_comets_per_wave);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Maximum number of comets. Default is 10.\n");
+  }
+  fprintf(fp, "max_comets = %d\n", game_options->max_comets);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Allow speed and number of comets to increase with each\n"
+                 "# wave.  May want to turn this off for smaller kids. Default is 1.\n");
+  }
+  fprintf(fp, "allow_speedup = %d\n", game_options->allow_speedup);
+
+  if(verbose)
+  {
+    fprintf (fp, "\n# Go back to starting speed and number of comets if player\n"
+                 "# misses a question. Useful for smaller kids. Default is 0.\n");
+  }
+  fprintf(fp, "slow_after_wrong = %d\n", game_options->slow_after_wrong);
+
+/*
+  fprintf(fp, "num_cities = %d\n", game_options->num_cities);
+  fprintf(fp, "num_bkgds = %d\n", game_options->num_bkgds);
+  fprintf(fp, "max_city_colors = %d\n", game_options->max_city_colors);
+*/
 }
 
 /* Set the application's icon: */

Modified: tuxmath/trunk/src/setup.h
===================================================================
--- tuxmath/trunk/src/setup.h	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/setup.h	2006-08-11 16:27:38 UTC (rev 20)
@@ -22,10 +22,13 @@
 #define SETUP_H
 
 #include <SDL.h>
+
 #ifndef NOSOUND
 #include <SDL_mixer.h>
 #endif
+
 #include "game.h"
+#include "tuxmath.h"
 
 extern SDL_Surface * screen;
 extern SDL_Surface * images[];
@@ -33,13 +36,11 @@
 extern Mix_Chunk * sounds[];
 extern Mix_Music * musics[];
 #endif
-/* extern int use_sound, fullscreen, use_bkgd, demo_mode, oper_override,
-  use_keypad, allow_neg_answer;*/
-/*extern float speed;*/
+
 extern int opers[NUM_OPERS], range_enabled[NUM_Q_RANGES];
-/*extern int max_answer;*/
 
 void setup(int argc, char * argv[]);
 void cleanup(void);
 
+void print_game_options(FILE* fp, int verbose);
 #endif

Modified: tuxmath/trunk/src/title.c
===================================================================
--- tuxmath/trunk/src/title.c	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/title.c	2006-08-11 16:27:38 UTC (rev 20)
@@ -166,13 +166,39 @@
 	    {
 	      key = event.key.keysym.sym;
 	      
-	      if (key == SDLK_ESCAPE)
+	      if ((key == SDLK_ESCAPE)
+                || (key == SDLK_q))
 		{
-		  /* Escape key - quit! */
+		  /* Escape key or 'Q' - quit! */
 		  
 		  cmd = CMD_QUIT;
 		  done = 1;
 		}
+
+	      if (key == SDLK_p)
+		{
+		  /* 'P'- play! */
+		  
+		  cmd = CMD_GAME;
+		  done = 1;
+		}
+
+	      if (key == SDLK_o)
+		{
+		  /* 'O'- Options! */
+		  
+		  cmd = CMD_OPTIONS;
+		  done = 1;
+		}
+
+	      if (key == SDLK_c)
+		{
+		  /* 'C'- Credits! */
+		  
+		  cmd = CMD_CREDITS;
+		  done = 1;
+		}
+
 	      else if (key == SDLK_DOWN)
 		{
 		  demo_countdown = START_DEMO_COUNTDOWN;
@@ -191,7 +217,9 @@
 		  if (cmd < 0)
 		    cmd = 0;
 		}
-	      else if (key == SDLK_RETURN || SDLK_KP_ENTER)
+	      else if ((key == SDLK_RETURN) 
+                    || (key == SDLK_KP_ENTER)
+                    || (key == SDLK_SPACE))
 		{
 		  done = 1;
 		}

Modified: tuxmath/trunk/src/tuxmath.h
===================================================================
--- tuxmath/trunk/src/tuxmath.h	2006-07-21 09:52:01 UTC (rev 19)
+++ tuxmath/trunk/src/tuxmath.h	2006-08-11 16:27:38 UTC (rev 20)
@@ -21,26 +21,28 @@
 #define TUXMATH_H
 
 //#define NOSOUND
-#define TUXMATH_DEBUG   /* for conditional compilation of debugging output */
-#define TUXMATH_VERSION 0.8
+//#define TUXMATH_DEBUG   /* for conditional compilation of debugging output */
+#define TUXMATH_VERSION 0.9
 
 /* this struct contains all options regarding general       */
 /* gameplay but not having to do with math questions per se */
+/* FIXME document what all of these options do */
 typedef struct game_option_type {
   /* general game options */
+  int per_user_config;
   int use_sound;
   int fullscreen;
   int use_bkgd;
   int demo_mode;
   int oper_override;
   int use_keypad;
-  int reuse_questions;
   float speed;
   int allow_speedup;
   float speedup_factor;
   float max_speed;
   int slow_after_wrong;
-  int extra_comets;
+  int starting_comets;
+  int extra_comets_per_wave;
   int max_comets;  /*FIXME not being used */
   /* not sure the rest of these belong in here */
   int num_cities;  /* MUST BE AN EVEN NUMBER! */
@@ -53,20 +55,21 @@
 extern game_option_type* game_options; /* used by setup.c, options.c, game.c */
 
 /* default values for game_options */
+#define DEFAULT_PER_USER_CONFIG 1
 #define DEFAULT_USE_SOUND 1
-#define DEFAULT_FULLSCREEN 0
+#define DEFAULT_FULLSCREEN 1
 #define DEFAULT_USE_BKGD 1
 #define DEFAULT_DEMO_MODE 0
 #define DEFAULT_OPER_OVERRIDE 0
 #define DEFAULT_USE_KEYPAD 0
-#define DEFAULT_REUSE_QUESTIONS 0
 #define DEFAULT_SPEED 1
 #define DEFAULT_ALLOW_SPEEDUP 1
 #define DEFAULT_SPEEDUP_FACTOR 1.2
 #define DEFAULT_MAX_SPEED 10
 #define DEFAULT_SLOW_AFTER_WRONG 0
-#define DEFAULT_EXTRA_COMETS 2
-#define DEFAULT_MAX_COMETS 4	
+#define DEFAULT_STARTING_COMETS 2
+#define DEFAULT_EXTRA_COMETS_PER_WAVE 2
+#define DEFAULT_MAX_COMETS 10	
 #define DEFAULT_NUM_CITIES 4   /* MUST BE AN EVEN NUMBER! */
 #define DEFAULT_NUM_BKGDS 5
 #define DEFAULT_MAX_CITY_COLORS 4




More information about the Tux4kids-commits mailing list